From: Sebastian Ramacher Date: Fri, 12 Sep 2025 21:00:35 +0000 (+0200) Subject: Import cmus_2.12.0-2.debian.tar.xz X-Git-Tag: archive/raspbian/2.12.0-2+rpi1^2~16^2 X-Git-Url: https://dgit.raspbian.org/%22http://www.example.com/cgi/%22/%22http:/www.example.com/cgi/%22?a=commitdiff_plain;h=138906403664a8a6edbe3cb6b330ca815e6ad53b;p=cmus.git Import cmus_2.12.0-2.debian.tar.xz [dgit import tarball cmus 2.12.0-2 cmus_2.12.0-2.debian.tar.xz] --- 138906403664a8a6edbe3cb6b330ca815e6ad53b diff --git a/changelog b/changelog new file mode 100644 index 0000000..38283a5 --- /dev/null +++ b/changelog @@ -0,0 +1,598 @@ +cmus (2.12.0-2) unstable; urgency=medium + + * Team upload + * Fix build with ffmpeg 8.0 (Closes: #1115013) + + -- Sebastian Ramacher Fri, 12 Sep 2025 23:00:35 +0200 + +cmus (2.12.0-1) unstable; urgency=medium + + * Team upload. + + [ Philippe SWARTVAGHER ] + * New upstream version 2.12.0 (Closes: #1087737) + + Refresh patches + * Bump standards-version to 4.7.2: no change needed + * d/rules: remove as-needed linker flag + * Add lintian override for library-not-linked-against-libc + of usr/lib/cmus/op/sndio.so + * Add patch to use hardening flags to build Doc/ttman + + [ Helmut Grohne ] + * Fix FTCBFS: Fix build/host confusion. (Closes: #954749) + + [ Dylan Aïssi ] + * Convert debian/copyright to DEP5 + + -- Philippe SWARTVAGHER Sat, 15 Mar 2025 22:56:48 +0100 + +cmus (2.11.0-1) unstable; urgency=medium + + * Team upload + * New upstream version 2.11.0 + - Fix build with ffmpeg 7.0 (Closes: #1072405) + * debian/patches: Refresh patches + * debian/control: + - Bump Standards-Version + - Use pkgconf + - Use openmpt's modplug + - Use libncurses-dev + + -- Sebastian Ramacher Sun, 09 Jun 2024 23:45:17 +0200 + +cmus (2.10.0-4) unstable; urgency=medium + + * Team upload + * debian/patches: Apply upstream patch to fix compatibility with ffmpeg 6.0 + (Closes: #1041375) + + -- Sebastian Ramacher Thu, 27 Jul 2023 21:00:31 +0200 + +cmus (2.10.0-3) unstable; urgency=medium + + * Team upload + * debian/control: Bump Standards-Version + * debian/: Remove roar support (Closes: #1030661) + + -- Sebastian Ramacher Tue, 07 Feb 2023 09:20:50 +0100 + +cmus (2.10.0-2) unstable; urgency=medium + + * Team upload + * Add a patch from upstream to fix a freeze when exiting cmus + + -- Philippe SWARTVAGHER Sun, 31 Jul 2022 22:01:04 +0200 + +cmus (2.10.0-1) unstable; urgency=medium + + * Team upload + + [ Jenkins ] + * Remove constraints unnecessary since buster + + [ Philippe SWARTVAGHER ] + * New upstream version 2.10.0 + * Bump d/watch version + * Bump standards-version to 4.6.1 + * Add a patch to fix a typo spotted by Lintian + + -- Philippe SWARTVAGHER Wed, 13 Jul 2022 20:40:37 +0200 + +cmus (2.9.1-1) unstable; urgency=medium + + * Team upload + * New upstream release + + -- Sebastian Ramacher Fri, 22 Jan 2021 22:28:04 +0100 + +cmus (2.9.0-1) unstable; urgency=medium + + * Team upload + + [ Debian Janitor ] + * Set upstream metadata fields: Bug-Database, Bug-Submit, Repository, + Repository-Browse. + + [ Patrick Gaskin ] + * Fix missing dependency for MPRIS support + + [ Sebastian Ramacher ] + * New upstream release + * debian/control: + - Bump Standards-Version + - Bump debhelper compat to 13 + - Set RRR: no + * debian/patches/12-typos.patch: Removed, fixed upstream + + -- Sebastian Ramacher Tue, 19 Jan 2021 20:15:26 +0100 + +cmus (2.8.0-2) unstable; urgency=medium + + * Set build flags via /usr/share/dpkg/buildflags.mk + * Link with -latomic to fix FTBFS on various architectures (Closes: #935678) + + -- Ryan Kavanagh Sat, 07 Sep 2019 10:37:13 -0400 + +cmus (2.8.0-1) unstable; urgency=medium + + [ Ondřej Nový ] + * Use debhelper-compat instead of debian/compat + + [ Helmut Grohne ] + * Fix FTCBFS: Supply a cross environment to ./configure. (Closes: #911163) + + [ Ryan Kavanagh ] + * New upstream version 2.8.0 (Closes: #932107, #783498) + + Update copyright file with new holders + + Drop 12-ffmpeg-4.0.patch (applied upstream) + + Refresh patches + * Drop outdated get-orig-source target from rules + * Fix typos in source, 12-typos.patch + * Enable hardening + * Bump debphelper compat to 12 + * Bump standards-version to 4.4.0 + * cmus-plugin-ffmpeg (<< 2.8.0) breaks cmus (>= 2.8.0) + + -- Ryan Kavanagh Sat, 17 Aug 2019 14:51:27 -0400 + +cmus (2.7.1+git20160225-2) unstable; urgency=medium + + * Team upload. + + [ James Cowgill ] + * Add upstream patch to fix FTBFS with FFmpeg 4.0. (Closes: #888384) + * d/changelog: Remove trailing blank line. + + [ Alessio Treglia ] + * Remove myself from the Uploaders field. + + [ Ondřej Nový ] + * d/control: Set Vcs-* to salsa.debian.org. + * d/changelog: Remove trailing whitespaces. + + [ Felipe Sateler ] + * Change maintainer address to debian-multimedia@lists.debian.org. + + -- James Cowgill Thu, 17 May 2018 11:34:35 +0100 + +cmus (2.7.1+git20160225-1) unstable; urgency=medium + + * Team upload. + * New upstream snapshot. + - Fix build against ffmpeg 3.0. (Closes: #810557) + * debian/patches/{01_config.mk.diff,02_link_avcodec.patch}: Removed, applied + upstream. + * debian/control: + - Bump Standards Version. + - Update Vcs-Git. + + -- Sebastian Ramacher Sun, 06 Mar 2016 21:37:57 +0100 + +cmus (2.7.1-1) unstable; urgency=medium + + * Team upload. + + [ Alessio Treglia ] + * Demote extra plugins to Suggests (Closes: #789256) + * Refresh patchset for 2.6.0. + + [ Sebastian Ramacher ] + * New upstream release. (Closes: #779335, #792134) + - Use libswresample instead of libavresample. (Closes: #805169, #805109) + * Update path for README + * debian/control: + - Add libdiscid-dev, libopusfile-dev, libsamplerate0-dev and libjack-dev + to Build-Depends. + - Change libavresample-dev to libswresample-dev in Build-Depends and add + libavcodec-dev. + - Add bash-completion to Build-Depends. + - Bump Standards-Version to 3.9.6. + - Update Vcs-Browser. + - Make cmus-plugin-ffmpeg depend on the same version of cmus (Closes: + #695072) + * debian/cmus.install: Install zsh completion. + * debian/cmus.bash-completion: Install bash completion. + * debian/rules: + - Build with --parallel and --with bash-completion. + - Handle jack shlibs similar to pulse. + * debian/patches: + - libav10.patch: Removed, no longer needed. + - 02_link_avcodec.patch: Functions from libavcodec are used so make sure + the ffmpeg plugin is linked against libavcodec. + + -- Sebastian Ramacher Sun, 15 Nov 2015 19:24:52 +0100 + +cmus (2.5.0-7) unstable; urgency=medium + + * Re-introduce Roaraudio support (Closes: #680745): + - debian/control: Add build-dependency on libroad-dev. + - debian/rules: Tune dpkg-shlibdeps call to move pulse and roar's + dependencies to Recommends. Made the whole mechanism slightly more + elegant. + * Enable CUE support. + * The project has moved to github, update the Homepage field accordingly. + * Update debian/watch, project has moved from sourceforge to github. + + -- Alessio Treglia Thu, 14 Aug 2014 13:45:10 +0100 + +cmus (2.5.0-6) unstable; urgency=medium + + * Team upload. + * Upload to unstable. + + -- Sebastian Ramacher Sun, 11 May 2014 23:45:16 +0200 + +cmus (2.5.0-5) experimental; urgency=low + + * Team upload. + * Compile against libav10 (Closes: #739301) + * Bump standards version + + -- Reinhard Tartler Mon, 24 Mar 2014 19:35:28 -0400 + +cmus (2.5.0-4) unstable; urgency=low + + [ Ryan Kavanagh ] + * Patches were applied upstream + + [ Alessio Treglia ] + * Add patch to prevent FTBFS. (Closes: #724181) + + -- Alessio Treglia Sun, 06 Oct 2013 20:46:25 +0100 + +cmus (2.5.0-3) unstable; urgency=low + + * Don't FTBFS due to missing config.mk (Closes: #720781), 01_config.mk.diff + * Fix typo in cmus binary, 02_fix_typo.diff + * Enable build hardening + + Bump debhelper version to 9.0.0 and compat to 9 + + Introduce 03_cppflags.diff to use CPPFLAGS; needed for function + fortification + + Override hardening-no-fortify-functions false positives + * Bump standards version to 3.9.4 + * Switch to canonical Vcs-* fields + * Enable verbose build logs + + -- Ryan Kavanagh Thu, 29 Aug 2013 13:48:30 -0400 + +cmus (2.5.0-2) unstable; urgency=low + + * Upload to unstable. + + -- Alessio Treglia Sat, 11 May 2013 01:21:18 +0200 + +cmus (2.5.0-1) experimental; urgency=low + + * New upstream release: + - CUE sheets support. + - cdio input plugin. + - support for WavPack `.wvc` correction files. + - new «zenburn» color scheme and text attributes (bold/reverse/...) + support for UI elements. + - improved tab completion, new scroll_offset and icecast_default_charset + options, even better tag parsing and compilations handling, and + numerous small enhancements all over the place. + * Build-depend on libcddb2-dev,libcdio-cdda-dev. + + -- Alessio Treglia Thu, 15 Nov 2012 00:28:20 +0000 + +cmus (2.4.3-2) unstable; urgency=low + + [ Ryan Kavanagh ] + * Update my email address to @debian.org + * Drop DM-Upload-Allowed: yes, no longer needed + + [ Alessio Treglia ] + * Build cmus without roar support. (Closes: #675610) + * Bump Standards. + + -- Alessio Treglia Sat, 02 Jun 2012 20:07:57 +0200 + +cmus (2.4.3-1) unstable; urgency=low + + * New upstream release. + + -- Alessio Treglia Sat, 03 Dec 2011 12:55:46 +0100 + +cmus (2.4.2-1) unstable; urgency=low + + * New upstream release. + * Drop 0001-fix-compile-error-for-new-versions-of-ffmpeg.patch, + applied upstream. + + -- Alessio Treglia Tue, 26 Jul 2011 10:13:25 +0200 + +cmus (2.4.1-2) unstable; urgency=low + + * Add Ubuntu-specific patch to fix FTBFS with newest version of ffmpeg. + * Replace negated list of architectures with linux-any (Closes: #634706). + + -- Alessio Treglia Sat, 23 Jul 2011 10:48:29 +0200 + +cmus (2.4.1-1) unstable; urgency=medium + + * New upstream release (Closes: #628422): + - Doc: add help for :shell + - ffmpeg: move up "config/ffmpeg.h" include + - fix two memleaks + - fix cache refresh bug + - configure: fix FLAC include path + - configure: fix ffmpeg header detection + - fix TCP/IP networking protocol + - fix segfault when hitting win-activate on empty tree + - display error if seeking failed + - fix segfault when using tqueue/lqueue + - fix lqueue command + - fix infinite loop when adding certain mp3 files + - fix reading of id3v2 tags at the end of files + - more fault-tolerant integer tag-reading + * Bump urgency to medium as the previous release was seriously buggy. + + -- Alessio Treglia Sun, 29 May 2011 19:18:48 +0200 + +cmus (2.4.0-1) unstable; urgency=low + + * New upstream release "Easter egg": + - Mutt-like short filters. + - Live filtering. + - Resume support. + - Smarter string handling. + - Long format options, including ones for bitrate/codec. + - HTTP proxy support for streams via http_proxy environment variable. + - Less CPU wakeups during playback. + - New RoarAudio output plugin. + - Support for big-endian systems, lots of different audio sample formats, + almost any C compiler and unix-like OS out there. + - Various bugfixes. + - Full release notes are available at: + http://sourceforge.net/mailarchive/message.php?msg_id=27403242 + * debian/watch: Properly handle release-candidate,beta releases. + * Remove debian/patches directory, all patches have been applied upstream. + * Bump Standards. + + -- Alessio Treglia Tue, 26 Apr 2011 23:30:07 +0200 + +cmus (2.3.5-1) unstable; urgency=low + + * New upstream release: + - Features gapless MP3 playback. + - Native PulseAudio support. + - Faster startup. + - Improve buildsystem. + * Refresh patches. + * Remo 21-missing_plugins.patch, applied upstream. + + -- Alessio Treglia Sat, 23 Apr 2011 09:56:57 +0200 + +cmus (2.3.4-3) unstable; urgency=low + + * Handle missing dependencies more gracefully: + - cmus silently skips plugins with missing dependencies, and instead + outputs a debug message. Original patch by Johannes Weißl, already + accepted upstream. + + -- Alessio Treglia Fri, 01 Apr 2011 08:59:01 +0200 + +cmus (2.3.4-2) unstable; urgency=low + + * Avoid to depend on several sound servers (Closes: #612887) and let + users choose to rely on the favorite one. + - debian/control: + + Add shlibs:Recommends field. + - debian/rules: + + Supply {dh_,dpkg-}shlibdeps with proper options to demote roar + and pulse audio dependencies to Recommends. + + -- Alessio Treglia Tue, 15 Mar 2011 12:46:03 +0100 + +cmus (2.3.4-1) unstable; urgency=low + + [ Ryan Kavanagh ] + * New upstream release. + * Dropped 01_spelling_mistakes.diff, 02_cmus-tutorial_whatis.diff + and 03-terminal_corruption.patch (applied upstream). + * Refreshed 10-roaraudio_support.patch + * Bump my copyright + + [ Alessio Treglia ] + * Add DM-Upload-Allowed: yes. + + -- Ryan Kavanagh Tue, 22 Feb 2011 09:03:23 -0500 + +cmus (2.3.3-4) unstable; urgency=low + + * Upload to unstable. + + -- Alessio Treglia Wed, 09 Feb 2011 12:05:49 +0100 + +cmus (2.3.3-3) experimental; urgency=low + + * Add RoarOutput plugin (Closes: #609202), thanks to + Philipp Schafft for the patch. + * Add patch taken from upstream's git to fix segfault when adding to + queue. + * Build-depends on libroar-dev (>= 0.4~beta2). + + -- Alessio Treglia Mon, 17 Jan 2011 02:23:04 +0100 + +cmus (2.3.3-2) unstable; urgency=low + + * Prevent terminal corruption on track change. + * debian/copyright: Update sources download location. + * Split cmus to provide smart dependencies (Closes: #442423). + * Bump Standards. + + -- Alessio Treglia Sun, 01 Aug 2010 12:26:08 +0200 + +cmus (2.3.3-1) unstable; urgency=low + + [ Ryan Kavanagh ] + * New upstream release (Closes: #572284) + * Imported Upstream version 2.3.3 + * Changed to source format 3.0 source. Involved converting + dpatch->quilt and dropping dpatch B-D. + * Dropped 01_cmusffmpeg.diff, no longer needed. Upstream checks for + ffmpeg and falls back to libavcodec for us. + * Dropped 02_cmusstatusdisplay.diff, included upstream. + * Dropped Yavor's mpcdec patch. Upstream expanded on it, using his + code if building under MPC SV8, the old code otherwise. + * Updated debian/watch + * Updated copyright file with new copyright holders and download + location. + * This is a new upload, package will thus be rebuilt against libavformat. + (Closes: #568361). + * Dropped debian/patches directory + * Move to debhelper 7 rules + * Now standards-version 3.8.4 + * Fix debhelper-but-no-misc-depends lintian warning + * Override dh_auto_configure because upstream uses a homebrewed configure + script that doesn't accept --a=b style options + * Bump debhelper version to (>= 7.0.50~) because we're using override_dh_* + * Fix spelling mistakes in binary and documentation + (01_spelling_mistake.diff) + * Fix whatis entry for cmus-tutorial manpage (02_cmus-tutorial_whatis.diff) + * Added Julien Louis' and my own packaging copyright blurb to + debian/copyright. + + [ Alessio Treglia ] + * This isn't a non-maintainer upload, we adopt this (Closes: #587604). + * Add debian/gbp.conf file. + * debian/control: + - Add Vcs-* tags. + - Bump Standards. + - Lines should be shorter than 80 characters. + - Add myself to Uploaders field, I'll take care of sponsoring this in + future. + - Build-depend on pkg-config. + - Drop unnecessary Recommends field. + * Drop aRTs support as it is no longer maintained. + * Add PulseAudio support. + * debian/rules: + - Call configure script instead of relying on dh_auto_configure. + * Drop README.source, we don't rely on dpatch as patch system. + * Update debian/copyright. + + -- Alessio Treglia Sun, 04 Jul 2010 20:06:58 +0200 + +cmus (2.2.0-4.1) unstable; urgency=low + + * NMU + * Patch from Yavor Doganov to port cmus to the new mpcdec API, + thereby allowing cmus to build from source again. + closes: #476382, #552820. + + -- Clint Adams Sun, 31 Jan 2010 00:03:40 -0500 + +cmus (2.2.0-4) unstable; urgency=low + + * Updated debian/watch file Closes: #449897 + - Thanks to Raphael Geissert + * Fix the ffmpeg/avcodec.h includes Closes: #517570 + - Thanks to Cyril Brulebois + * Added Recommends libasound2, libartsc0, libao2 Closes: #439719 + * Added Depends line in debian/control + * Added dpatch on Build-Depends on debian/control + * Deleted commented lines on debian/rules + * Update debian/rules for dpatch dependence + * Changed debian/control + - Standard-Version to 3.8.1 ( was 3.8.0 ) + - Added debian/README.source + - Updated to debhelper to 7 + - Updated debian/compat (was 5) + - Added Homepage field + + -- Carlos Eduardo Sotelo Pinto (krlos) Thu, 19 Mar 2009 13:38:16 -0500 + +cmus (2.2.0-3) unstable; urgency=low + + * Acknowledging NMU. Closes: #509277. + + -- Carlos Eduardo Sotelo Pinto (krlos) Mon, 29 Dec 2008 22:01:01 +0100 + +cmus (2.2.0-2) unstable; urgency=low + + * New maintainer. Closes: #484734 + * Changed debian/control + - Standard-Version to 3.8.0 ( was 3.7.2 no changes needed ) + + -- Carlos Eduardo Sotelo Pinto (krlos) Wed, 03 Sep 2008 17:46:50 -0500 + +cmus (2.2.0-1.1) unstable; urgency=high + + * Non-maintainer upload by the Security Team. + * Modify example script cmus-status-display to write the current + status to .cmus-status in the user's home instead of /tmp/cmus-status, + since the latter could lead to symlink attacks. CVE-2008-5375 + (Closes: #509277) + + -- Moritz Muehlenhoff Sun, 28 Dec 2008 14:57:06 +0100 + +cmus (2.2.0-1) unstable; urgency=low + + * New upstream release + * Add libwavpack-dev and libavformat.dev to Build-Depends + * Update debian/copyright + + -- Julien Louis Fri, 27 Jul 2007 21:54:36 +0200 + +cmus (2.1.0-2) unstable; urgency=low + + * Rebuild against libflac8 (Closes: #426638). + + -- Julien Louis Mon, 04 Jun 2007 22:56:58 +0200 + +cmus (2.1.0-1) unstable; urgency=low + + * New upstream release (closes: #399965). + * Updated debian/copyright + * Added libfaad-dev to Build-Depends + + -- Julien Louis Thu, 21 Dec 2006 20:25:59 +0100 + +cmus (2.0.4-1) unstable; urgency=low + + * New upstream release. + * Added debian/watch file. + * Build-Depends agains libasound2-dev (>= 1.0.11). + + -- Julien Louis Wed, 23 Aug 2006 03:34:04 +0200 + +cmus (2.0.3-3) unstable; urgency=low + + * Drop libasound2-dev Build Dependency on non-linux arches + (Closes: #377885). + + -- Julien Louis Wed, 12 Jul 2006 01:06:02 +0200 + +cmus (2.0.3-2) unstable; urgency=low + + * Move all dh_* helper stuff in the binary-arch target (Closes: #376320). + * Remove debian/patches/01_asciidoc_xsl_path.dpatch since it is not usefull. + * Remove dpatch from Build-Depends. + + -- Julien Louis Sun, 2 Jul 2006 14:16:03 +0200 + +cmus (2.0.3-1) unstable; urgency=low + + * New upstream release. + * Remove asciidoc, docbook-xsl, doxbook-xml ans xsltproc from Build-Depends. + * Add libartsc0-dev and libao-dev to Build-Depends. + + -- Julien Louis Sun, 18 Jun 2006 17:26:44 +0200 + +cmus (2.0.2-1) unstable; urgency=low + + * New upstream release. + * Remove ASCIIDOC patch since upstream now search for + /etc/asciidoc/docbook-xsl/. + * Add docbook-xml to Build-Depends. + * Bump Standards-Version no change needed. + * Added REAMDE.Debian + + -- Julien Louis Tue, 30 May 2006 22:12:01 +0200 + +cmus (2.0.0-1) unstable; urgency=low + + * Initial release (Closes: #340000) + + -- Julien Louis Mon, 7 Nov 2005 18:19:55 +0100 diff --git a/cmus.bash-completion b/cmus.bash-completion new file mode 100644 index 0000000..ca9c5f2 --- /dev/null +++ b/cmus.bash-completion @@ -0,0 +1 @@ +contrib/cmus.bash-completion cmus diff --git a/cmus.install b/cmus.install new file mode 100644 index 0000000..cb199e1 --- /dev/null +++ b/cmus.install @@ -0,0 +1,2 @@ +usr +contrib/_cmus usr/share/zsh/vendor-completions diff --git a/cmus.lintian-overrides b/cmus.lintian-overrides new file mode 100644 index 0000000..4ccc128 --- /dev/null +++ b/cmus.lintian-overrides @@ -0,0 +1,4 @@ +cmus: hardening-no-fortify-functions usr/lib/cmus/ip/flac.so + +## This library actually doesn't directly call any libc function +cmus: library-not-linked-against-libc [usr/lib/cmus/op/sndio.so] diff --git a/control b/control new file mode 100644 index 0000000..d33178e --- /dev/null +++ b/control @@ -0,0 +1,77 @@ +Source: cmus +Section: sound +Priority: optional +Maintainer: Debian Multimedia Maintainers +Uploaders: + Ryan Kavanagh +Build-Depends: + debhelper-compat (= 13), + bash-completion, + libao-dev, + libasound2-dev [linux-any], + libavcodec-dev, + libavformat-dev, + libswresample-dev, + libcddb2-dev, + libcdio-cdda-dev, + libcue-dev, + libdiscid-dev, + libfaad-dev, + libflac-dev, + libjack-dev, + libmad0-dev, + libmpcdec-dev, + libncurses-dev, + libopenmpt-modplug-dev, + libopusfile-dev, + libpulse-dev, + libsamplerate0-dev, + libsystemd-dev, + libvorbis-dev, + libwavpack-dev, + pkgconf +Standards-Version: 4.7.2 +Homepage: https://cmus.github.io/ +Vcs-Git: https://salsa.debian.org/multimedia-team/cmus.git +Vcs-Browser: https://salsa.debian.org/multimedia-team/cmus +Rules-Requires-Root: no + +Package: cmus +Architecture: any +Depends: + ${misc:Depends}, + ${shlibs:Depends} +Recommends: + cmus-plugin-ffmpeg +Suggests: + ${shlibs:Suggests} +Breaks: cmus-plugin-ffmpeg (<< 2.8.0) +Description: lightweight ncurses audio player + C* Music Player is a modular and very configurable ncurses-based audio player. + It has some interesting features like configurable colorscheme, mp3 and ogg + streaming, it can be controlled with an UNIX socket, filters, album/artists + sorting and a vi-like configuration interface. + . + It currently supports different input formats: + - Ogg Vorbis + - MP3 (with libmad) + - FLAC + - Wav + - Modules (with libmodplug) + - Musepack + - AAC + - Windows Media Audio + +Package: cmus-plugin-ffmpeg +Architecture: any +Depends: + ${misc:Depends}, + ${shlibs:Depends}, + cmus (= ${binary:Version}) +Description: lightweight ncurses audio player (FFmpeg plugin) + C* Music Player is a modular and very configurable ncurses-based audio player. + It has some interesting features like configurable colorscheme, mp3 and ogg + streaming, it can be controlled with an UNIX socket, filters, album/artists + sorting and a vi-like configuration interface. + . + This package adds FFmpeg support to C* Music Player. diff --git a/copyright b/copyright new file mode 100644 index 0000000..01793b0 --- /dev/null +++ b/copyright @@ -0,0 +1,38 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Source: https://github.com/cmus/cmus + +Files: * +Copyright: 2004-2007 Timo Hirvonen + 2009 Gregory Petrosyan + 1999 Paul N. Fisher + 2002 Andy Lo A Foe + 2006 dnk + 2006 Chun-Yu Shei + 2007 Kevin Ko + 2007 dnk + 2007 Johannes Weißl + 2016 Nic Soudée +License: GPL-2+ + +Files: debian/* +Copyright: 2005-2008 Julien Louis + 2010-2019 Ryan Kavanagh + 2010 Alessio Treglia +License: GPL-2+ + +License: GPL-2+ + This package is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + . + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + . + You should have received a copy of the GNU General Public License + along with this program. If not, see . + . + On Debian systems, the full text of the GNU General Public License + version 2 can be found in `/usr/share/common-licenses/GPL-2'. diff --git a/docs b/docs new file mode 100644 index 0000000..93c309d --- /dev/null +++ b/docs @@ -0,0 +1,2 @@ +README.md +contrib/ diff --git a/examples b/examples new file mode 100644 index 0000000..5a8a7b5 --- /dev/null +++ b/examples @@ -0,0 +1 @@ +cmus-status-display diff --git a/gbp.conf b/gbp.conf new file mode 100644 index 0000000..cec628c --- /dev/null +++ b/gbp.conf @@ -0,0 +1,2 @@ +[DEFAULT] +pristine-tar = True diff --git a/patches/0001-atomic_ld.patch b/patches/0001-atomic_ld.patch new file mode 100644 index 0000000..0ecab21 --- /dev/null +++ b/patches/0001-atomic_ld.patch @@ -0,0 +1,30 @@ +From: Ryan Kavanagh +Date: Thu, 27 Jul 2023 20:59:47 +0200 +Subject: Pass LDLIBS to the linker + +Origin: Debian +Bug-Debian: http://bugs.debian.org/935678 +Forwarded: no +Reviewed-by: Ryan Kavanagh +Last-Update: 2019-09-07 + +Needed to pass -latomic at the end so that we can fix a FTBFS on various +architectures. +Last-Update: 2019-09-07 +--- + Makefile | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/Makefile b/Makefile +index 76a4d59..720f043 100644 +--- a/Makefile ++++ b/Makefile +@@ -19,7 +19,7 @@ include scripts/lib.mk + CFLAGS += -D_FILE_OFFSET_BITS=64 + + CMUS_LIBS = $(PTHREAD_LIBS) $(NCURSES_LIBS) $(ICONV_LIBS) $(DL_LIBS) $(DISCID_LIBS) \ +- -lm $(COMPAT_LIBS) $(LIBSYSTEMD_LIBS) ++ -lm $(COMPAT_LIBS) $(LIBSYSTEMD_LIBS) $(LDLIBS) + + command_mode.o input.o main.o ui_curses.o op/pulse.lo: .version + command_mode.o input.o main.o ui_curses.o op/pulse.lo: CFLAGS += -DVERSION=\"$(VERSION)\" diff --git a/patches/0002-fix-blhc.patch b/patches/0002-fix-blhc.patch new file mode 100644 index 0000000..95dac1f --- /dev/null +++ b/patches/0002-fix-blhc.patch @@ -0,0 +1,22 @@ +Description: Use hardening flags for Doc/ttman +Author: Philippe SWARTVAGHER +Forwarded: not-needed +Last-Update: 2025-03-16 +--- +diff --git a/Makefile b/Makefile +index 76a4d59..3860f77 100644 +--- a/Makefile ++++ b/Makefile +@@ -255,12 +255,6 @@ $(man7): Doc/ttman + %.7: %.txt + $(call cmd,ttman) + +-Doc/ttman.o: Doc/ttman.c +- $(call cmd,hostcc,) +- +-Doc/ttman: Doc/ttman.o +- $(call cmd,hostld,) +- + quiet_cmd_ttman = MAN $@ + cmd_ttman = Doc/ttman $< $@ + # }}} diff --git a/patches/0003-ip-ffmpeg-more-precise-seeking.patch b/patches/0003-ip-ffmpeg-more-precise-seeking.patch new file mode 100644 index 0000000..e26e916 --- /dev/null +++ b/patches/0003-ip-ffmpeg-more-precise-seeking.patch @@ -0,0 +1,229 @@ +From: ihy123 +Date: Thu, 14 Aug 2025 12:44:10 +0300 +Subject: ip/ffmpeg: more precise seeking + +av_seek_frame() and avformat_seek_file() seek to nearest "keyframe". For +codecs like, for example, ape this means that seeking will be very off +(5 seconds or more). So what we do is: +1. seek to nearest "keyframe" before the desired time, +2. discard some frames to approach the desired time. +--- + ip/ffmpeg.c | 154 +++++++++++++++++++++++++++++++++++++----------------------- + 1 file changed, 94 insertions(+), 60 deletions(-) + +diff --git a/ip/ffmpeg.c b/ip/ffmpeg.c +index 21b9a01..ecbf005 100644 +--- a/ip/ffmpeg.c ++++ b/ip/ffmpeg.c +@@ -44,6 +44,8 @@ struct ffmpeg_input { + AVPacket pkt; + int curr_pkt_size; + uint8_t *curr_pkt_buf; ++ int64_t seek_ts; ++ int64_t prev_frame_end; + int stream_index; + + unsigned long curr_size; +@@ -76,6 +78,8 @@ static struct ffmpeg_input *ffmpeg_input_create(void) + return NULL; + } + input->curr_pkt_size = 0; ++ input->seek_ts = -1; ++ input->prev_frame_end = -1; + input->curr_pkt_buf = input->pkt.data; + return input; + } +@@ -314,10 +318,7 @@ static int ffmpeg_fill_buffer(struct input_plugin_data *ip_data, AVFormatContext + #else + AVFrame *frame = avcodec_alloc_frame(); + #endif +- int got_frame; + while (1) { +- int len; +- + if (input->curr_pkt_size <= 0) { + #if LIBAVCODEC_VERSION_MAJOR >= 56 + av_packet_unref(&input->pkt); +@@ -333,78 +334,108 @@ static int ffmpeg_fill_buffer(struct input_plugin_data *ip_data, AVFormatContext + #endif + return 0; + } +- if (input->pkt.stream_index == input->stream_index) { +- input->curr_pkt_size = input->pkt.size; +- input->curr_pkt_buf = input->pkt.data; +- input->curr_size += input->pkt.size; +- input->curr_duration += input->pkt.duration; +- } +- continue; +- } + +- { +- AVPacket avpkt; +- av_new_packet(&avpkt, input->curr_pkt_size); +- memcpy(avpkt.data, input->curr_pkt_buf, input->curr_pkt_size); ++ if (input->pkt.stream_index != input->stream_index) ++ continue; ++ input->curr_pkt_size = input->pkt.size; ++ input->curr_pkt_buf = input->pkt.data; ++ input->curr_size += input->pkt.size; ++ input->curr_duration += input->pkt.duration; ++ + #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101) +- int send_result = avcodec_send_packet(cc, &avpkt); +- if (send_result != 0) { +- if (send_result != AVERROR(EAGAIN)) { +- d_print("avcodec_send_packet() returned %d\n", send_result); +- char errstr[AV_ERROR_MAX_STRING_SIZE]; +- if (!av_strerror(send_result, errstr, AV_ERROR_MAX_STRING_SIZE )) +- { +- d_print("av_strerror(): %s\n", errstr); +- } else { +- d_print("av_strerror(): Description for error cannot be found\n"); +- } +- av_packet_unref(&avpkt); +- return -IP_ERROR_INTERNAL; ++ int send_result = avcodec_send_packet(cc, &input->pkt); ++ if (send_result != 0 && send_result != AVERROR(EAGAIN)) { ++ d_print("avcodec_send_packet() returned %d\n", send_result); ++ char errstr[AV_ERROR_MAX_STRING_SIZE]; ++ if (!av_strerror(send_result, errstr, AV_ERROR_MAX_STRING_SIZE )) ++ { ++ d_print("av_strerror(): %s\n", errstr); ++ } else { ++ d_print("av_strerror(): Description for error cannot be found\n"); + } +- len = 0; +- } else { +- len = input->curr_pkt_size; ++ return -IP_ERROR_INTERNAL; + } +- +- int recv_result = avcodec_receive_frame(cc, frame); +- got_frame = (recv_result == 0) ? 1 : 0; +-#else +- len = avcodec_decode_audio4(cc, frame, &got_frame, &avpkt); +-#endif +-#if LIBAVCODEC_VERSION_MAJOR >= 56 +- av_packet_unref(&avpkt); +-#else +- av_free_packet(&avpkt); + #endif + } ++ ++#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101) ++ int recv_result = avcodec_receive_frame(cc, frame); ++ if (recv_result < 0) { ++ input->curr_pkt_size = 0; ++ continue; ++ } ++#else ++ int got_frame; ++ int len = avcodec_decode_audio4(cc, frame, &got_frame, &input->pkt); + if (len < 0) { + /* this is often reached when seeking, not sure why */ + input->curr_pkt_size = 0; + continue; + } +- input->curr_pkt_size -= len; +- input->curr_pkt_buf += len; +- if (got_frame) { +- int res = swr_convert(swr, +- &output->buffer, +- frame->nb_samples, +- (const uint8_t **)frame->extended_data, +- frame->nb_samples); +- if (res < 0) +- res = 0; +- output->buffer_pos = output->buffer; ++ if (!got_frame) ++ continue; ++#endif ++ ++ int64_t frame_ts = -1; ++ if (frame->pts) ++ frame_ts = frame->pts; ++ else if (frame->pkt_pts) ++ frame_ts = frame->pkt_pts; ++ else if (frame->pkt_dts) ++ frame_ts = frame->pkt_dts; ++ ++ const uint8_t **in = (const uint8_t **)frame->extended_data; ++ int in_count = frame->nb_samples; ++ if (input->seek_ts > 0 && (frame_ts >= 0 || input->prev_frame_end >= 0)) { ++ struct ffmpeg_private *priv = ip_data->private; ++ AVStream *st = priv->input_context->streams[priv->input->stream_index]; ++ if (frame_ts >= 0) ++ frame_ts = av_rescale_q(frame_ts, st->time_base, AV_TIME_BASE_Q); ++ else ++ frame_ts = input->prev_frame_end; ++ int64_t frame_dur = av_rescale(frame->nb_samples, AV_TIME_BASE, sf_get_rate(ip_data->sf)); ++ int64_t frame_end = frame_ts + frame_dur; ++ input->prev_frame_end = frame_end; ++ d_print("seek_ts: %ld, frame_ts: %ld, frame_end: %ld\n", input->seek_ts, frame_ts, frame_end); ++ if (frame_end <= input->seek_ts) ++ continue; ++ ++ /* skip part of this frame */ ++ int64_t skip_samples = av_rescale(input->seek_ts - frame_ts, sf_get_rate(ip_data->sf), AV_TIME_BASE); ++ in_count -= skip_samples; ++ if (av_sample_fmt_is_planar(frame->format)) { ++ for (int i = 0; i < cc->channels; i++) { ++ in[i] += skip_samples * sf_get_sample_size(ip_data->sf); ++ } ++ } else { ++ *in += skip_samples * cc->channels * sf_get_sample_size(ip_data->sf); ++ } ++ ++ input->seek_ts = -1; ++ input->prev_frame_end = -1; ++ } ++ ++ int res = swr_convert(swr, ++ &output->buffer, ++ frame->nb_samples, ++ in, ++ in_count); ++ if (res < 0) ++ res = 0; ++ ++ output->buffer_pos = output->buffer; + #if LIBAVCODEC_VERSION_MAJOR >= 60 +- output->buffer_used_len = res * cc->ch_layout.nb_channels * sf_get_sample_size(ip_data->sf); ++ output->buffer_used_len = res * cc->ch_layout.nb_channels * sf_get_sample_size(ip_data->sf); + #else +- output->buffer_used_len = res * cc->channels * sf_get_sample_size(ip_data->sf); ++ output->buffer_used_len = res * cc->channels * sf_get_sample_size(ip_data->sf); + #endif ++ + #if LIBAVCODEC_VERSION_MAJOR >= 56 +- av_frame_free(&frame); ++ av_frame_free(&frame); + #else +- avcodec_free_frame(&frame); ++ avcodec_free_frame(&frame); + #endif +- return output->buffer_used_len; +- } ++ return output->buffer_used_len; + } + /* This should never get here. */ + return -IP_ERROR_INTERNAL; +@@ -437,13 +468,16 @@ static int ffmpeg_seek(struct input_plugin_data *ip_data, double offset) + AVStream *st = priv->input_context->streams[priv->input->stream_index]; + int ret; + +- int64_t pts = av_rescale_q(offset * AV_TIME_BASE, AV_TIME_BASE_Q, st->time_base); ++ priv->input->seek_ts = offset * AV_TIME_BASE; ++ priv->input->prev_frame_end = -1; ++ int64_t ts = av_rescale(offset, st->time_base.den, st->time_base.num); + + avcodec_flush_buffers(priv->codec_context); + /* Force reading a new packet in next ffmpeg_fill_buffer(). */ + priv->input->curr_pkt_size = 0; + +- ret = av_seek_frame(priv->input_context, priv->input->stream_index, pts, 0); ++ ret = avformat_seek_file(priv->input_context, ++ priv->input->stream_index, 0, ts, ts, 0); + + if (ret < 0) { + return -IP_ERROR_FUNCTION_NOT_SUPPORTED; diff --git a/patches/0004-ip-ffmpeg-skip-samples-only-when-needed.patch b/patches/0004-ip-ffmpeg-skip-samples-only-when-needed.patch new file mode 100644 index 0000000..1e0a634 --- /dev/null +++ b/patches/0004-ip-ffmpeg-skip-samples-only-when-needed.patch @@ -0,0 +1,53 @@ +From: ihy123 +Date: Fri, 15 Aug 2025 21:42:19 +0300 +Subject: ip/ffmpeg: skip samples only when needed + +--- + ip/ffmpeg.c | 32 ++++++++++++++++++-------------- + 1 file changed, 18 insertions(+), 14 deletions(-) + +diff --git a/ip/ffmpeg.c b/ip/ffmpeg.c +index ecbf005..5f5a4f3 100644 +--- a/ip/ffmpeg.c ++++ b/ip/ffmpeg.c +@@ -393,22 +393,26 @@ static int ffmpeg_fill_buffer(struct input_plugin_data *ip_data, AVFormatContext + frame_ts = av_rescale_q(frame_ts, st->time_base, AV_TIME_BASE_Q); + else + frame_ts = input->prev_frame_end; +- int64_t frame_dur = av_rescale(frame->nb_samples, AV_TIME_BASE, sf_get_rate(ip_data->sf)); +- int64_t frame_end = frame_ts + frame_dur; +- input->prev_frame_end = frame_end; +- d_print("seek_ts: %ld, frame_ts: %ld, frame_end: %ld\n", input->seek_ts, frame_ts, frame_end); +- if (frame_end <= input->seek_ts) +- continue; + +- /* skip part of this frame */ +- int64_t skip_samples = av_rescale(input->seek_ts - frame_ts, sf_get_rate(ip_data->sf), AV_TIME_BASE); +- in_count -= skip_samples; +- if (av_sample_fmt_is_planar(frame->format)) { +- for (int i = 0; i < cc->channels; i++) { +- in[i] += skip_samples * sf_get_sample_size(ip_data->sf); ++ if (frame_ts < input->seek_ts) { ++ int64_t frame_dur = av_rescale(frame->nb_samples, AV_TIME_BASE, sf_get_rate(ip_data->sf)); ++ int64_t frame_end = frame_ts + frame_dur; ++ input->prev_frame_end = frame_end; ++ d_print("seek_ts: %ld, frame_ts: %ld, frame_end: %ld\n", input->seek_ts, frame_ts, frame_end); ++ if (frame_end <= input->seek_ts) ++ continue; ++ ++ /* skip part of this frame */ ++ int64_t skip_samples = av_rescale(input->seek_ts - frame_ts, sf_get_rate(ip_data->sf), AV_TIME_BASE); ++ in_count -= skip_samples; ++ if (av_sample_fmt_is_planar(frame->format)) { ++ for (int i = 0; i < cc->channels; i++) { ++ in[i] += skip_samples * sf_get_sample_size(ip_data->sf); ++ } ++ } else { ++ *in += skip_samples * cc->channels * sf_get_sample_size(ip_data->sf); + } +- } else { +- *in += skip_samples * cc->channels * sf_get_sample_size(ip_data->sf); ++ d_print("skipping %ld samples\n", skip_samples); + } + + input->seek_ts = -1; diff --git a/patches/0005-ip-ffmpeg-remove-excessive-version-checks.patch b/patches/0005-ip-ffmpeg-remove-excessive-version-checks.patch new file mode 100644 index 0000000..20aebf0 --- /dev/null +++ b/patches/0005-ip-ffmpeg-remove-excessive-version-checks.patch @@ -0,0 +1,342 @@ +From: ihy123 +Date: Sat, 16 Aug 2025 02:43:55 +0300 +Subject: ip/ffmpeg: remove excessive version checks + +ffmpeg download page states that v4.0.6 has +- libavutil 56.14.100 +- libavcodec 58.18.100 +- libavformat 58.12.100 +(https://ffmpeg.org/olddownload.html) + +After removing all checks for versions lower than these, the plugin +still compiles with v3.3.9 headers. + +After all, why be better with compatibility than developers themselves? +--- + ip/ffmpeg.c | 109 +++++++++++++----------------------------------------------- + 1 file changed, 23 insertions(+), 86 deletions(-) + +diff --git a/ip/ffmpeg.c b/ip/ffmpeg.c +index 5f5a4f3..f6a11f4 100644 +--- a/ip/ffmpeg.c ++++ b/ip/ffmpeg.c +@@ -25,7 +25,6 @@ + #include "../config/ffmpeg.h" + #endif + +-#include + #include + #include + #include +@@ -43,7 +42,6 @@ + struct ffmpeg_input { + AVPacket pkt; + int curr_pkt_size; +- uint8_t *curr_pkt_buf; + int64_t seek_ts; + int64_t prev_frame_end; + int stream_index; +@@ -80,17 +78,12 @@ static struct ffmpeg_input *ffmpeg_input_create(void) + input->curr_pkt_size = 0; + input->seek_ts = -1; + input->prev_frame_end = -1; +- input->curr_pkt_buf = input->pkt.data; + return input; + } + + static void ffmpeg_input_free(struct ffmpeg_input *input) + { +-#if LIBAVCODEC_VERSION_MAJOR >= 56 + av_packet_unref(&input->pkt); +-#else +- av_free_packet(&input->pkt); +-#endif + free(input); + } + +@@ -132,7 +125,7 @@ static void ffmpeg_init(void) + + av_log_set_level(AV_LOG_QUIET); + +-#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 18, 100) ++#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 9, 100) + /* We could register decoders explicitly to save memory, but we have to + * be careful about compatibility. */ + av_register_all(); +@@ -149,9 +142,7 @@ static int ffmpeg_open(struct input_plugin_data *ip_data) + AVCodec const *codec; + AVCodecContext *cc = NULL; + AVFormatContext *ic = NULL; +-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101) + AVCodecParameters *cp = NULL; +-#endif + SwrContext *swr = NULL; + + ffmpeg_init(); +@@ -171,20 +162,11 @@ static int ffmpeg_open(struct input_plugin_data *ip_data) + } + + for (i = 0; i < ic->nb_streams; i++) { +- +-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101) + cp = ic->streams[i]->codecpar; + if (cp->codec_type == AVMEDIA_TYPE_AUDIO) { + stream_index = i; + break; + } +-#else +- cc = ic->streams[i]->codec; +- if (cc->codec_type == AVMEDIA_TYPE_AUDIO) { +- stream_index = i; +- break; +- } +-#endif + } + + if (stream_index == -1) { +@@ -193,13 +175,9 @@ static int ffmpeg_open(struct input_plugin_data *ip_data) + break; + } + +-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101) + codec = avcodec_find_decoder(cp->codec_id); + cc = avcodec_alloc_context3(codec); + avcodec_parameters_to_context(cc, cp); +-#else +- codec = avcodec_find_decoder(cc->codec_id); +-#endif + if (!codec) { + d_print("codec not found: %d, %s\n", cc->codec_id, avcodec_get_name(cc->codec_id)); + err = -IP_ERROR_UNSUPPORTED_FILE_TYPE; +@@ -217,9 +195,7 @@ static int ffmpeg_open(struct input_plugin_data *ip_data) + + if (err < 0) { + /* Clean up. cc is never opened at this point. (See above assumption.) */ +-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101) + avcodec_free_context(&cc); +-#endif + avformat_close_input(&ic); + return err; + } +@@ -231,9 +207,7 @@ static int ffmpeg_open(struct input_plugin_data *ip_data) + priv->input = ffmpeg_input_create(); + if (priv->input == NULL) { + avcodec_close(cc); +-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101) + avcodec_free_context(&cc); +-#endif + avformat_close_input(&ic); + free(priv); + return -IP_ERROR_INTERNAL; +@@ -244,7 +218,7 @@ static int ffmpeg_open(struct input_plugin_data *ip_data) + /* Prepare for resampling. */ + out_sample_rate = min_u(cc->sample_rate, 384000); + swr = swr_alloc(); +-#if LIBAVCODEC_VERSION_MAJOR >= 60 ++#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(59, 24, 100) + if (cc->ch_layout.order == AV_CHANNEL_ORDER_UNSPEC) + av_channel_layout_default(&cc->ch_layout, cc->ch_layout.nb_channels); + av_opt_set_chlayout(swr, "in_chlayout", &cc->ch_layout, 0); +@@ -259,7 +233,7 @@ static int ffmpeg_open(struct input_plugin_data *ip_data) + priv->swr = swr; + + ip_data->private = priv; +-#if LIBAVCODEC_VERSION_MAJOR >= 60 ++#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(59, 24, 100) + ip_data->sf = sf_rate(out_sample_rate) | sf_channels(cc->ch_layout.nb_channels); + #else + ip_data->sf = sf_rate(out_sample_rate) | sf_channels(cc->channels); +@@ -281,10 +255,12 @@ static int ffmpeg_open(struct input_plugin_data *ip_data) + } + swr_init(swr); + ip_data->sf |= sf_host_endian(); +-#if LIBAVCODEC_VERSION_MAJOR >= 60 +- channel_map_init_waveex(cc->ch_layout.nb_channels, cc->ch_layout.u.mask, ip_data->channel_map); ++#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(59, 24, 100) ++ channel_map_init_waveex(cc->ch_layout.nb_channels, ++ cc->ch_layout.u.mask, ip_data->channel_map); + #else +- channel_map_init_waveex(cc->channels, cc->channel_layout, ip_data->channel_map); ++ channel_map_init_waveex(cc->channels, ++ cc->channel_layout, ip_data->channel_map); + #endif + return 0; + } +@@ -294,9 +270,7 @@ static int ffmpeg_close(struct input_plugin_data *ip_data) + struct ffmpeg_private *priv = ip_data->private; + + avcodec_close(priv->codec_context); +-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101) + avcodec_free_context(&priv->codec_context); +-#endif + avformat_close_input(&priv->input_context); + swr_free(&priv->swr); + ffmpeg_input_free(priv->input); +@@ -310,39 +284,27 @@ static int ffmpeg_close(struct input_plugin_data *ip_data) + * This returns the number of bytes added to the buffer. + * It returns < 0 on error. 0 on EOF. + */ +-static int ffmpeg_fill_buffer(struct input_plugin_data *ip_data, AVFormatContext *ic, AVCodecContext *cc, +- struct ffmpeg_input *input, struct ffmpeg_output *output, SwrContext *swr) ++static int ffmpeg_fill_buffer(struct input_plugin_data *ip_data, ++ AVFormatContext *ic, AVCodecContext *cc, ++ struct ffmpeg_input *input, struct ffmpeg_output *output, ++ SwrContext *swr) + { +-#if LIBAVCODEC_VERSION_MAJOR >= 56 + AVFrame *frame = av_frame_alloc(); +-#else +- AVFrame *frame = avcodec_alloc_frame(); +-#endif + while (1) { + if (input->curr_pkt_size <= 0) { +-#if LIBAVCODEC_VERSION_MAJOR >= 56 + av_packet_unref(&input->pkt); +-#else +- av_free_packet(&input->pkt); +-#endif + if (av_read_frame(ic, &input->pkt) < 0) { + /* Force EOF once we can read no longer. */ +-#if LIBAVCODEC_VERSION_MAJOR >= 56 + av_frame_free(&frame); +-#else +- avcodec_free_frame(&frame); +-#endif + return 0; + } + + if (input->pkt.stream_index != input->stream_index) + continue; + input->curr_pkt_size = input->pkt.size; +- input->curr_pkt_buf = input->pkt.data; + input->curr_size += input->pkt.size; + input->curr_duration += input->pkt.duration; + +-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101) + int send_result = avcodec_send_packet(cc, &input->pkt); + if (send_result != 0 && send_result != AVERROR(EAGAIN)) { + d_print("avcodec_send_packet() returned %d\n", send_result); +@@ -355,32 +317,17 @@ static int ffmpeg_fill_buffer(struct input_plugin_data *ip_data, AVFormatContext + } + return -IP_ERROR_INTERNAL; + } +-#endif + } + +-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101) + int recv_result = avcodec_receive_frame(cc, frame); + if (recv_result < 0) { + input->curr_pkt_size = 0; + continue; + } +-#else +- int got_frame; +- int len = avcodec_decode_audio4(cc, frame, &got_frame, &input->pkt); +- if (len < 0) { +- /* this is often reached when seeking, not sure why */ +- input->curr_pkt_size = 0; +- continue; +- } +- if (!got_frame) +- continue; +-#endif + + int64_t frame_ts = -1; + if (frame->pts) + frame_ts = frame->pts; +- else if (frame->pkt_pts) +- frame_ts = frame->pkt_pts; + else if (frame->pkt_dts) + frame_ts = frame->pkt_dts; + +@@ -395,7 +342,7 @@ static int ffmpeg_fill_buffer(struct input_plugin_data *ip_data, AVFormatContext + frame_ts = input->prev_frame_end; + + if (frame_ts < input->seek_ts) { +- int64_t frame_dur = av_rescale(frame->nb_samples, AV_TIME_BASE, sf_get_rate(ip_data->sf)); ++ int64_t frame_dur = av_rescale(frame->nb_samples, AV_TIME_BASE, frame->sample_rate); + int64_t frame_end = frame_ts + frame_dur; + input->prev_frame_end = frame_end; + d_print("seek_ts: %ld, frame_ts: %ld, frame_end: %ld\n", input->seek_ts, frame_ts, frame_end); +@@ -403,14 +350,14 @@ static int ffmpeg_fill_buffer(struct input_plugin_data *ip_data, AVFormatContext + continue; + + /* skip part of this frame */ +- int64_t skip_samples = av_rescale(input->seek_ts - frame_ts, sf_get_rate(ip_data->sf), AV_TIME_BASE); ++ int64_t skip_samples = av_rescale(input->seek_ts - frame_ts, frame->sample_rate, AV_TIME_BASE); + in_count -= skip_samples; + if (av_sample_fmt_is_planar(frame->format)) { +- for (int i = 0; i < cc->channels; i++) { ++ for (int i = 0; i < sf_get_channels(ip_data->sf); i++) { + in[i] += skip_samples * sf_get_sample_size(ip_data->sf); + } + } else { +- *in += skip_samples * cc->channels * sf_get_sample_size(ip_data->sf); ++ *in += skip_samples * sf_get_frame_size(ip_data->sf); + } + d_print("skipping %ld samples\n", skip_samples); + } +@@ -428,17 +375,9 @@ static int ffmpeg_fill_buffer(struct input_plugin_data *ip_data, AVFormatContext + res = 0; + + output->buffer_pos = output->buffer; +-#if LIBAVCODEC_VERSION_MAJOR >= 60 +- output->buffer_used_len = res * cc->ch_layout.nb_channels * sf_get_sample_size(ip_data->sf); +-#else +- output->buffer_used_len = res * cc->channels * sf_get_sample_size(ip_data->sf); +-#endif ++ output->buffer_used_len = res * sf_get_frame_size(ip_data->sf); + +-#if LIBAVCODEC_VERSION_MAJOR >= 56 + av_frame_free(&frame); +-#else +- avcodec_free_frame(&frame); +-#endif + return output->buffer_used_len; + } + /* This should never get here. */ +@@ -453,11 +392,11 @@ static int ffmpeg_read(struct input_plugin_data *ip_data, char *buffer, int coun + int out_size; + + if (output->buffer_used_len == 0) { +- rc = ffmpeg_fill_buffer(ip_data, priv->input_context, priv->codec_context, ++ rc = ffmpeg_fill_buffer(ip_data, ++ priv->input_context, priv->codec_context, + priv->input, priv->output, priv->swr); +- if (rc <= 0) { ++ if (rc <= 0) + return rc; +- } + } + out_size = min_i(output->buffer_used_len, count); + memcpy(buffer, output->buffer_pos, out_size); +@@ -477,6 +416,7 @@ static int ffmpeg_seek(struct input_plugin_data *ip_data, double offset) + int64_t ts = av_rescale(offset, st->time_base.den, st->time_base.num); + + avcodec_flush_buffers(priv->codec_context); ++ /* TODO: also flush swresample buffers */ + /* Force reading a new packet in next ffmpeg_fill_buffer(). */ + priv->input->curr_pkt_size = 0; + +@@ -501,7 +441,8 @@ static void ffmpeg_read_metadata(struct growing_keyvals *c, AVDictionary *metada + } + } + +-static int ffmpeg_read_comments(struct input_plugin_data *ip_data, struct keyval **comments) ++static int ffmpeg_read_comments(struct input_plugin_data *ip_data, ++ struct keyval **comments) + { + struct ffmpeg_private *priv = ip_data->private; + AVFormatContext *ic = priv->input_context; +@@ -538,11 +479,7 @@ static long ffmpeg_current_bitrate(struct input_plugin_data *ip_data) + AVStream *st = priv->input_context->streams[priv->input->stream_index]; + long bitrate = -1; + /* ape codec returns silly numbers */ +-#if LIBAVCODEC_VERSION_MAJOR >= 55 + if (priv->codec->id == AV_CODEC_ID_APE) +-#else +- if (priv->codec->id == CODEC_ID_APE) +-#endif + return -1; + if (priv->input->curr_duration > 0) { + double seconds = priv->input->curr_duration * av_q2d(st->time_base); diff --git a/patches/0006-ip-ffmpeg-major-refactor.patch b/patches/0006-ip-ffmpeg-major-refactor.patch new file mode 100644 index 0000000..507c654 --- /dev/null +++ b/patches/0006-ip-ffmpeg-major-refactor.patch @@ -0,0 +1,777 @@ +From: ihy123 +Date: Sun, 17 Aug 2025 04:05:36 +0300 +Subject: ip/ffmpeg: major refactor + +--- + ip/ffmpeg.c | 643 +++++++++++++++++++++++++++++++----------------------------- + 1 file changed, 330 insertions(+), 313 deletions(-) + +diff --git a/ip/ffmpeg.c b/ip/ffmpeg.c +index f6a11f4..42f630e 100644 +--- a/ip/ffmpeg.c ++++ b/ip/ffmpeg.c +@@ -35,84 +35,32 @@ + #include + #endif + +-#ifndef AVCODEC_MAX_AUDIO_FRAME_SIZE +-#define AVCODEC_MAX_AUDIO_FRAME_SIZE 192000 +-#endif ++struct ffmpeg_private { ++ AVCodecContext *codec_ctx; ++ AVFormatContext *format_ctx; ++ AVCodec const *codec; ++ SwrContext *swr; ++ int stream_index; + +-struct ffmpeg_input { +- AVPacket pkt; +- int curr_pkt_size; ++ AVPacket *pkt; ++ AVFrame *frame; + int64_t seek_ts; + int64_t prev_frame_end; +- int stream_index; + ++ /* A buffer to hold swr_convert()-ed samples */ ++ AVFrame *swr_frame; ++ int swr_frame_start; ++ ++ /* Bitrate estimation */ + unsigned long curr_size; + unsigned long curr_duration; + }; + +-struct ffmpeg_output { +- uint8_t *buffer; +- uint8_t *buffer_malloc; +- uint8_t *buffer_pos; /* current buffer position */ +- int buffer_used_len; +-}; +- +-struct ffmpeg_private { +- AVCodecContext *codec_context; +- AVFormatContext *input_context; +- AVCodec const *codec; +- SwrContext *swr; +- +- struct ffmpeg_input *input; +- struct ffmpeg_output *output; +-}; +- +-static struct ffmpeg_input *ffmpeg_input_create(void) +-{ +- struct ffmpeg_input *input = xnew(struct ffmpeg_input, 1); +- +- if (av_new_packet(&input->pkt, 0) != 0) { +- free(input); +- return NULL; +- } +- input->curr_pkt_size = 0; +- input->seek_ts = -1; +- input->prev_frame_end = -1; +- return input; +-} +- +-static void ffmpeg_input_free(struct ffmpeg_input *input) +-{ +- av_packet_unref(&input->pkt); +- free(input); +-} +- +-static struct ffmpeg_output *ffmpeg_output_create(void) +-{ +- struct ffmpeg_output *output = xnew(struct ffmpeg_output, 1); +- +- output->buffer_malloc = xnew(uint8_t, AVCODEC_MAX_AUDIO_FRAME_SIZE + 15); +- output->buffer = output->buffer_malloc; +- /* align to 16 bytes so avcodec can SSE/Altivec/etc */ +- while ((intptr_t) output->buffer % 16) +- output->buffer += 1; +- output->buffer_pos = output->buffer; +- output->buffer_used_len = 0; +- return output; +-} +- +-static void ffmpeg_output_free(struct ffmpeg_output *output) +-{ +- free(output->buffer_malloc); +- output->buffer_malloc = NULL; +- output->buffer = NULL; +- free(output); +-} +- +-static inline void ffmpeg_buffer_flush(struct ffmpeg_output *output) ++static const char *ffmpeg_errmsg(int err) + { +- output->buffer_pos = output->buffer; +- output->buffer_used_len = 0; ++ static char errstr[AV_ERROR_MAX_STRING_SIZE]; ++ av_strerror(err, errstr, AV_ERROR_MAX_STRING_SIZE); ++ return errstr; + } + + static void ffmpeg_init(void) +@@ -132,303 +80,372 @@ static void ffmpeg_init(void) + #endif + } + +-static int ffmpeg_open(struct input_plugin_data *ip_data) ++static int ffmpeg_open_input(struct input_plugin_data *ip_data, ++ struct ffmpeg_private *priv) + { +- struct ffmpeg_private *priv; +- int err = 0; +- int i; +- int stream_index = -1; +- int out_sample_rate; +- AVCodec const *codec; +- AVCodecContext *cc = NULL; + AVFormatContext *ic = NULL; ++ AVCodecContext *cc = NULL; + AVCodecParameters *cp = NULL; +- SwrContext *swr = NULL; +- +- ffmpeg_init(); ++ AVCodec const *codec = NULL; ++ int stream_index = -1; + +- err = avformat_open_input(&ic, ip_data->filename, NULL, NULL); +- if (err < 0) { +- d_print("av_open failed: %d\n", err); +- return -IP_ERROR_FILE_FORMAT; ++ int err; ++ int res = avformat_open_input(&ic, ip_data->filename, NULL, NULL); ++ if (res < 0) { ++ err = -IP_ERROR_FILE_FORMAT; ++ goto err; + } + +- do { +- err = avformat_find_stream_info(ic, NULL); +- if (err < 0) { +- d_print("unable to find stream info: %d\n", err); +- err = -IP_ERROR_FILE_FORMAT; +- break; +- } +- +- for (i = 0; i < ic->nb_streams; i++) { +- cp = ic->streams[i]->codecpar; +- if (cp->codec_type == AVMEDIA_TYPE_AUDIO) { +- stream_index = i; +- break; +- } +- } +- +- if (stream_index == -1) { +- d_print("could not find audio stream\n"); +- err = -IP_ERROR_FILE_FORMAT; +- break; +- } +- +- codec = avcodec_find_decoder(cp->codec_id); +- cc = avcodec_alloc_context3(codec); +- avcodec_parameters_to_context(cc, cp); +- if (!codec) { +- d_print("codec not found: %d, %s\n", cc->codec_id, avcodec_get_name(cc->codec_id)); +- err = -IP_ERROR_UNSUPPORTED_FILE_TYPE; +- break; +- } ++ res = avformat_find_stream_info(ic, NULL); ++ if (res < 0) { ++ d_print("unable to find stream info\n"); ++ err = -IP_ERROR_FILE_FORMAT; ++ goto err; ++ } + +- if (avcodec_open2(cc, codec, NULL) < 0) { +- d_print("could not open codec: %d, %s\n", cc->codec_id, avcodec_get_name(cc->codec_id)); +- err = -IP_ERROR_UNSUPPORTED_FILE_TYPE; ++ for (int i = 0; i < ic->nb_streams; i++) { ++ cp = ic->streams[i]->codecpar; ++ if (cp->codec_type == AVMEDIA_TYPE_AUDIO) { ++ stream_index = i; + break; + } ++ } + +- /* We assume below that no more errors follow. */ +- } while (0); ++ if (stream_index == -1) { ++ d_print("could not find audio stream\n"); ++ err = -IP_ERROR_FILE_FORMAT; ++ goto err_silent; ++ } + +- if (err < 0) { +- /* Clean up. cc is never opened at this point. (See above assumption.) */ +- avcodec_free_context(&cc); +- avformat_close_input(&ic); +- return err; ++ codec = avcodec_find_decoder(cp->codec_id); ++ if (!codec) { ++ d_print("codec (id: %d, name: %s) not found\n", ++ cc->codec_id, avcodec_get_name(cc->codec_id)); ++ err = -IP_ERROR_UNSUPPORTED_FILE_TYPE; ++ goto err_silent; ++ } ++ cc = avcodec_alloc_context3(codec); ++ avcodec_parameters_to_context(cc, cp); ++ ++ res = avcodec_open2(cc, codec, NULL); ++ if (res < 0) { ++ d_print("could not open codec (id: %d, name: %s)\n", ++ cc->codec_id, avcodec_get_name(cc->codec_id)); ++ err = -IP_ERROR_UNSUPPORTED_FILE_TYPE; ++ goto err; + } + +- priv = xnew(struct ffmpeg_private, 1); +- priv->codec_context = cc; +- priv->input_context = ic; ++ priv->format_ctx = ic; ++ priv->codec_ctx = cc; + priv->codec = codec; +- priv->input = ffmpeg_input_create(); +- if (priv->input == NULL) { +- avcodec_close(cc); +- avcodec_free_context(&cc); +- avformat_close_input(&ic); +- free(priv); +- return -IP_ERROR_INTERNAL; ++ priv->stream_index = stream_index; ++ return 0; ++err: ++ d_print("%s\n", ffmpeg_errmsg(res)); ++err_silent: ++ avcodec_free_context(&cc); ++ avformat_close_input(&ic); ++ return err; ++} ++ ++static void ffmpeg_set_sf_and_swr_opts(SwrContext *swr, AVCodecContext *cc, ++ sample_format_t *sf_out, enum AVSampleFormat *out_sample_fmt) ++{ ++ int out_sample_rate = min_u(cc->sample_rate, 384000); ++ sample_format_t sf = sf_rate(out_sample_rate) | sf_host_endian(); ++ av_opt_set_int(swr, "in_sample_rate", cc->sample_rate, 0); ++ av_opt_set_int(swr, "out_sample_rate", out_sample_rate, 0); ++ ++ *out_sample_fmt = cc->sample_fmt; ++ switch (*out_sample_fmt) { ++ case AV_SAMPLE_FMT_U8: ++ sf |= sf_bits(8) | sf_signed(0); ++ break; ++ case AV_SAMPLE_FMT_S32: ++ sf |= sf_bits(32) | sf_signed(1); ++ break; ++ default: ++ sf |= sf_bits(16) | sf_signed(1); ++ *out_sample_fmt = AV_SAMPLE_FMT_S16; + } +- priv->input->stream_index = stream_index; +- priv->output = ffmpeg_output_create(); ++ av_opt_set_sample_fmt(swr, "in_sample_fmt", cc->sample_fmt, 0); ++ av_opt_set_sample_fmt(swr, "out_sample_fmt", *out_sample_fmt, 0); + +- /* Prepare for resampling. */ +- out_sample_rate = min_u(cc->sample_rate, 384000); +- swr = swr_alloc(); + #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(59, 24, 100) ++ sf |= sf_channels(cc->ch_layout.nb_channels); ++ + if (cc->ch_layout.order == AV_CHANNEL_ORDER_UNSPEC) + av_channel_layout_default(&cc->ch_layout, cc->ch_layout.nb_channels); +- av_opt_set_chlayout(swr, "in_chlayout", &cc->ch_layout, 0); +- av_opt_set_chlayout(swr, "out_chlayout", &cc->ch_layout, 0); ++ av_opt_set_chlayout(swr, "in_chlayout", &cc->ch_layout, 0); ++ av_opt_set_chlayout(swr, "out_chlayout", &cc->ch_layout, 0); + #else +- av_opt_set_int(swr, "in_channel_layout", av_get_default_channel_layout(cc->channels), 0); +- av_opt_set_int(swr, "out_channel_layout", av_get_default_channel_layout(cc->channels), 0); ++ sf |= sf_channels(cc->channels); ++ ++ av_opt_set_int(swr, "in_channel_layout", ++ av_get_default_channel_layout(cc->channels), 0); ++ av_opt_set_int(swr, "out_channel_layout", ++ av_get_default_channel_layout(cc->channels), 0); + #endif +- av_opt_set_int(swr, "in_sample_rate", cc->sample_rate, 0); +- av_opt_set_int(swr, "out_sample_rate", out_sample_rate, 0); +- av_opt_set_sample_fmt(swr, "in_sample_fmt", cc->sample_fmt, 0); +- priv->swr = swr; + +- ip_data->private = priv; ++ *sf_out = sf; ++} ++ ++static int ffmpeg_init_swr_frame(struct ffmpeg_private *priv, ++ sample_format_t sf, enum AVSampleFormat out_sample_fmt) ++{ ++ AVCodecContext *cc = priv->codec_ctx; ++ AVFrame *frame = av_frame_alloc(); ++ + #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(59, 24, 100) +- ip_data->sf = sf_rate(out_sample_rate) | sf_channels(cc->ch_layout.nb_channels); ++ av_channel_layout_copy(&frame->ch_layout, &cc->ch_layout); + #else +- ip_data->sf = sf_rate(out_sample_rate) | sf_channels(cc->channels); ++ frame->channel_layout = av_get_default_channel_layout(cc->channels); + #endif +- switch (cc->sample_fmt) { +- case AV_SAMPLE_FMT_U8: +- ip_data->sf |= sf_bits(8) | sf_signed(0); +- av_opt_set_sample_fmt(swr, "out_sample_fmt", AV_SAMPLE_FMT_U8, 0); +- break; +- case AV_SAMPLE_FMT_S32: +- ip_data->sf |= sf_bits(32) | sf_signed(1); +- av_opt_set_sample_fmt(swr, "out_sample_fmt", AV_SAMPLE_FMT_S32, 0); +- break; +- /* AV_SAMPLE_FMT_S16 */ +- default: +- ip_data->sf |= sf_bits(16) | sf_signed(1); +- av_opt_set_sample_fmt(swr, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0); +- break; ++ ++ frame->sample_rate = sf_get_rate(sf); ++ frame->format = out_sample_fmt; ++ ++ /* NOTE: 10 sec is probably too much, but the amount of space ++ * needed for swr_convert() is unpredictable */ ++ frame->nb_samples = 10 * sf_get_rate(sf); ++ int res = av_frame_get_buffer(frame, 0); ++ if (res < 0) { ++ d_print("av_frame_get_buffer(): %s\n", ffmpeg_errmsg(res)); ++ return -IP_ERROR_INTERNAL; + } +- swr_init(swr); +- ip_data->sf |= sf_host_endian(); ++ frame->nb_samples = 0; ++ ++ priv->swr_frame = frame; ++ return 0; ++} ++ ++static void ffmpeg_free(struct ffmpeg_private *priv) ++{ ++ avcodec_close(priv->codec_ctx); ++ avcodec_free_context(&priv->codec_ctx); ++ avformat_close_input(&priv->format_ctx); ++ ++ swr_free(&priv->swr); ++ ++ av_frame_free(&priv->frame); ++ av_packet_free(&priv->pkt); ++ av_frame_free(&priv->swr_frame); ++} ++ ++static int ffmpeg_open(struct input_plugin_data *ip_data) ++{ ++ struct ffmpeg_private priv; ++ enum AVSampleFormat out_sample_fmt; ++ memset(&priv, 0, sizeof(struct ffmpeg_private)); ++ ++ ffmpeg_init(); ++ ++ int err = ffmpeg_open_input(ip_data, &priv); ++ if (err < 0) ++ return err; ++ ++ priv.pkt = av_packet_alloc(); ++ priv.frame = av_frame_alloc(); ++ priv.seek_ts = -1; ++ priv.prev_frame_end = -1; ++ ++ priv.swr = swr_alloc(); ++ ffmpeg_set_sf_and_swr_opts(priv.swr, priv.codec_ctx, ++ &ip_data->sf, &out_sample_fmt); ++ swr_init(priv.swr); ++ ++ err = ffmpeg_init_swr_frame(&priv, ip_data->sf, out_sample_fmt); ++ if (err < 0) { ++ ffmpeg_free(&priv); ++ return err; ++ } ++ + #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(59, 24, 100) +- channel_map_init_waveex(cc->ch_layout.nb_channels, +- cc->ch_layout.u.mask, ip_data->channel_map); ++ channel_map_init_waveex(priv.codec_ctx->ch_layout.nb_channels, ++ priv.codec_ctx->ch_layout.u.mask, ip_data->channel_map); + #else +- channel_map_init_waveex(cc->channels, +- cc->channel_layout, ip_data->channel_map); ++ channel_map_init_waveex(priv.codec_ctx->channels, ++ priv.codec_ctx->channel_layout, ip_data->channel_map); + #endif ++ ++ ip_data->private = xnew(struct ffmpeg_private, 1); ++ memcpy(ip_data->private, &priv, sizeof(struct ffmpeg_private)); + return 0; + } + + static int ffmpeg_close(struct input_plugin_data *ip_data) + { +- struct ffmpeg_private *priv = ip_data->private; +- +- avcodec_close(priv->codec_context); +- avcodec_free_context(&priv->codec_context); +- avformat_close_input(&priv->input_context); +- swr_free(&priv->swr); +- ffmpeg_input_free(priv->input); +- ffmpeg_output_free(priv->output); +- free(priv); ++ ffmpeg_free(ip_data->private); ++ free(ip_data->private); + ip_data->private = NULL; + return 0; + } + + /* +- * This returns the number of bytes added to the buffer. +- * It returns < 0 on error. 0 on EOF. ++ * return: ++ * 0 - retry ++ * >0 - ok + */ +-static int ffmpeg_fill_buffer(struct input_plugin_data *ip_data, +- AVFormatContext *ic, AVCodecContext *cc, +- struct ffmpeg_input *input, struct ffmpeg_output *output, +- SwrContext *swr) ++static int ffmpeg_seek_into_frame(struct ffmpeg_private *priv, int64_t frame_ts) + { +- AVFrame *frame = av_frame_alloc(); +- while (1) { +- if (input->curr_pkt_size <= 0) { +- av_packet_unref(&input->pkt); +- if (av_read_frame(ic, &input->pkt) < 0) { +- /* Force EOF once we can read no longer. */ +- av_frame_free(&frame); +- return 0; +- } +- +- if (input->pkt.stream_index != input->stream_index) +- continue; +- input->curr_pkt_size = input->pkt.size; +- input->curr_size += input->pkt.size; +- input->curr_duration += input->pkt.duration; +- +- int send_result = avcodec_send_packet(cc, &input->pkt); +- if (send_result != 0 && send_result != AVERROR(EAGAIN)) { +- d_print("avcodec_send_packet() returned %d\n", send_result); +- char errstr[AV_ERROR_MAX_STRING_SIZE]; +- if (!av_strerror(send_result, errstr, AV_ERROR_MAX_STRING_SIZE )) +- { +- d_print("av_strerror(): %s\n", errstr); +- } else { +- d_print("av_strerror(): Description for error cannot be found\n"); +- } +- return -IP_ERROR_INTERNAL; +- } +- } ++ if (frame_ts >= 0) { ++ AVStream *s = priv->format_ctx->streams[priv->stream_index]; ++ frame_ts = av_rescale_q(frame_ts, s->time_base, AV_TIME_BASE_Q); ++ } else { ++ frame_ts = priv->prev_frame_end; ++ } + +- int recv_result = avcodec_receive_frame(cc, frame); +- if (recv_result < 0) { +- input->curr_pkt_size = 0; +- continue; +- } ++ if (frame_ts >= priv->seek_ts) ++ return 1; + +- int64_t frame_ts = -1; +- if (frame->pts) +- frame_ts = frame->pts; +- else if (frame->pkt_dts) +- frame_ts = frame->pkt_dts; +- +- const uint8_t **in = (const uint8_t **)frame->extended_data; +- int in_count = frame->nb_samples; +- if (input->seek_ts > 0 && (frame_ts >= 0 || input->prev_frame_end >= 0)) { +- struct ffmpeg_private *priv = ip_data->private; +- AVStream *st = priv->input_context->streams[priv->input->stream_index]; +- if (frame_ts >= 0) +- frame_ts = av_rescale_q(frame_ts, st->time_base, AV_TIME_BASE_Q); +- else +- frame_ts = input->prev_frame_end; +- +- if (frame_ts < input->seek_ts) { +- int64_t frame_dur = av_rescale(frame->nb_samples, AV_TIME_BASE, frame->sample_rate); +- int64_t frame_end = frame_ts + frame_dur; +- input->prev_frame_end = frame_end; +- d_print("seek_ts: %ld, frame_ts: %ld, frame_end: %ld\n", input->seek_ts, frame_ts, frame_end); +- if (frame_end <= input->seek_ts) +- continue; +- +- /* skip part of this frame */ +- int64_t skip_samples = av_rescale(input->seek_ts - frame_ts, frame->sample_rate, AV_TIME_BASE); +- in_count -= skip_samples; +- if (av_sample_fmt_is_planar(frame->format)) { +- for (int i = 0; i < sf_get_channels(ip_data->sf); i++) { +- in[i] += skip_samples * sf_get_sample_size(ip_data->sf); +- } +- } else { +- *in += skip_samples * sf_get_frame_size(ip_data->sf); +- } +- d_print("skipping %ld samples\n", skip_samples); +- } +- +- input->seek_ts = -1; +- input->prev_frame_end = -1; +- } ++ int64_t frame_dur = av_rescale(priv->frame->nb_samples, ++ AV_TIME_BASE, priv->frame->sample_rate); ++ int64_t frame_end = frame_ts + frame_dur; ++ priv->prev_frame_end = frame_end; ++ ++ d_print("seek_ts: %ld, frame_ts: %ld, frame_end: %ld\n", ++ priv->seek_ts, frame_ts, frame_end); ++ ++ if (frame_end <= priv->seek_ts) ++ return 0; ++ ++ int64_t skip_samples = av_rescale(priv->seek_ts - frame_ts, ++ priv->frame->sample_rate, AV_TIME_BASE); ++ priv->frame->nb_samples -= skip_samples; ++ ++ int bps = av_get_bytes_per_sample(priv->frame->format); ++#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(59, 24, 100) ++ int channels = priv->codec_ctx->ch_layout.nb_channels; ++#else ++ int channels = priv->codec_ctx->channels; ++#endif ++ ++ /* Just modify frame's data pointer because it's throw-away */ ++ if (av_sample_fmt_is_planar(priv->frame->format)) { ++ for (int i = 0; i < channels; i++) ++ priv->frame->extended_data[i] += skip_samples * bps; ++ } else { ++ priv->frame->extended_data[0] += skip_samples * channels * bps; ++ } ++ d_print("skipping %ld samples\n", skip_samples); ++ return 1; ++} + +- int res = swr_convert(swr, +- &output->buffer, +- frame->nb_samples, +- in, +- in_count); ++/* ++ * return: ++ * <0 - error ++ * 0 - retry ++ * >0 - ok ++ */ ++static int ffmpeg_get_frame(struct ffmpeg_private *priv) ++{ ++ int res = avcodec_receive_frame(priv->codec_ctx, priv->frame); ++ if (res == AVERROR(EAGAIN)) { ++ av_packet_unref(priv->pkt); ++ res = av_read_frame(priv->format_ctx, priv->pkt); + if (res < 0) +- res = 0; ++ return res; ++ ++ if (priv->pkt->stream_index != priv->stream_index) ++ return 0; + +- output->buffer_pos = output->buffer; +- output->buffer_used_len = res * sf_get_frame_size(ip_data->sf); ++ priv->curr_size += priv->pkt->size; ++ priv->curr_duration += priv->pkt->duration; + +- av_frame_free(&frame); +- return output->buffer_used_len; ++ res = avcodec_send_packet(priv->codec_ctx, priv->pkt); ++ if (res == AVERROR(EAGAIN)) ++ return 0; + } +- /* This should never get here. */ +- return -IP_ERROR_INTERNAL; ++ if (res < 0) ++ return res; ++ ++ int64_t frame_ts = -1; ++ if (priv->frame->pts >= 0) ++ frame_ts = priv->frame->pts; ++ else if (priv->frame->pkt_dts >= 0) ++ frame_ts = priv->frame->pkt_dts; ++ ++ if (priv->seek_ts > 0 && (frame_ts >= 0 || priv->prev_frame_end >= 0)) { ++ if (ffmpeg_seek_into_frame(priv, frame_ts) == 0) ++ return 0; ++ priv->seek_ts = -1; ++ priv->prev_frame_end = -1; ++ } ++ return 1; ++} ++ ++static int ffmpeg_convert_frame(struct ffmpeg_private *priv) ++{ ++ int res = swr_convert(priv->swr, ++ priv->swr_frame->extended_data, ++ /* TODO: proper buffer capacity */ ++ priv->frame->nb_samples, ++ (const uint8_t **)priv->frame->extended_data, ++ priv->frame->nb_samples); ++ if (res >= 0) { ++ priv->swr_frame->nb_samples = res; ++ priv->swr_frame_start = 0; ++ } ++ return res; + } + + static int ffmpeg_read(struct input_plugin_data *ip_data, char *buffer, int count) + { + struct ffmpeg_private *priv = ip_data->private; +- struct ffmpeg_output *output = priv->output; +- int rc; +- int out_size; +- +- if (output->buffer_used_len == 0) { +- rc = ffmpeg_fill_buffer(ip_data, +- priv->input_context, priv->codec_context, +- priv->input, priv->output, priv->swr); +- if (rc <= 0) +- return rc; ++ int written = 0; ++ int res; ++ ++ count /= sf_get_frame_size(ip_data->sf); ++ ++ while (count) { ++ if (priv->swr_frame->nb_samples == 0) { ++ res = ffmpeg_get_frame(priv); ++ if (res == AVERROR_EOF) ++ break; ++ else if (res == 0) ++ continue; ++ else if (res < 0) ++ goto err; ++ ++ res = ffmpeg_convert_frame(priv); ++ if (res < 0) ++ goto err; ++ } ++ ++ int copy_frames = min_i(count, priv->swr_frame->nb_samples); ++ int copy_bytes = copy_frames * sf_get_frame_size(ip_data->sf); ++ void *dst = priv->swr_frame->extended_data[0] + priv->swr_frame_start; ++ memcpy(buffer + written, dst, copy_bytes); ++ ++ priv->swr_frame->nb_samples -= copy_frames; ++ priv->swr_frame_start += copy_bytes; ++ count -= copy_frames; ++ written += copy_bytes; + } +- out_size = min_i(output->buffer_used_len, count); +- memcpy(buffer, output->buffer_pos, out_size); +- output->buffer_used_len -= out_size; +- output->buffer_pos += out_size; +- return out_size; ++ return written; ++err: ++ d_print("%s\n", ffmpeg_errmsg(res)); ++ return -IP_ERROR_INTERNAL; + } + + static int ffmpeg_seek(struct input_plugin_data *ip_data, double offset) + { + struct ffmpeg_private *priv = ip_data->private; +- AVStream *st = priv->input_context->streams[priv->input->stream_index]; +- int ret; ++ AVStream *st = priv->format_ctx->streams[priv->stream_index]; + +- priv->input->seek_ts = offset * AV_TIME_BASE; +- priv->input->prev_frame_end = -1; ++ priv->seek_ts = offset * AV_TIME_BASE; ++ priv->prev_frame_end = -1; + int64_t ts = av_rescale(offset, st->time_base.den, st->time_base.num); + +- avcodec_flush_buffers(priv->codec_context); +- /* TODO: also flush swresample buffers */ +- /* Force reading a new packet in next ffmpeg_fill_buffer(). */ +- priv->input->curr_pkt_size = 0; +- +- ret = avformat_seek_file(priv->input_context, +- priv->input->stream_index, 0, ts, ts, 0); +- +- if (ret < 0) { ++ int ret = avformat_seek_file(priv->format_ctx, ++ priv->stream_index, 0, ts, ts, 0); ++ if (ret < 0) + return -IP_ERROR_FUNCTION_NOT_SUPPORTED; +- } else { +- ffmpeg_buffer_flush(priv->output); +- return 0; +- } ++ ++ priv->swr_frame->nb_samples = 0; ++ avcodec_flush_buffers(priv->codec_ctx); ++ /* also flush swresample buffers? */ ++ return 0; + } + + static void ffmpeg_read_metadata(struct growing_keyvals *c, AVDictionary *metadata) +@@ -445,7 +462,7 @@ static int ffmpeg_read_comments(struct input_plugin_data *ip_data, + struct keyval **comments) + { + struct ffmpeg_private *priv = ip_data->private; +- AVFormatContext *ic = priv->input_context; ++ AVFormatContext *ic = priv->format_ctx; + + GROWING_KEYVALS(c); + +@@ -463,29 +480,29 @@ static int ffmpeg_read_comments(struct input_plugin_data *ip_data, + static int ffmpeg_duration(struct input_plugin_data *ip_data) + { + struct ffmpeg_private *priv = ip_data->private; +- return priv->input_context->duration / AV_TIME_BASE; ++ return priv->format_ctx->duration / AV_TIME_BASE; + } + + static long ffmpeg_bitrate(struct input_plugin_data *ip_data) + { + struct ffmpeg_private *priv = ip_data->private; +- long bitrate = priv->input_context->bit_rate; ++ long bitrate = priv->format_ctx->bit_rate; + return bitrate ? bitrate : -IP_ERROR_FUNCTION_NOT_SUPPORTED; + } + + static long ffmpeg_current_bitrate(struct input_plugin_data *ip_data) + { + struct ffmpeg_private *priv = ip_data->private; +- AVStream *st = priv->input_context->streams[priv->input->stream_index]; ++ AVStream *st = priv->format_ctx->streams[priv->stream_index]; + long bitrate = -1; + /* ape codec returns silly numbers */ + if (priv->codec->id == AV_CODEC_ID_APE) + return -1; +- if (priv->input->curr_duration > 0) { +- double seconds = priv->input->curr_duration * av_q2d(st->time_base); +- bitrate = (8 * priv->input->curr_size) / seconds; +- priv->input->curr_size = 0; +- priv->input->curr_duration = 0; ++ if (priv->curr_duration > 0) { ++ double seconds = priv->curr_duration * av_q2d(st->time_base); ++ bitrate = (8 * priv->curr_size) / seconds; ++ priv->curr_size = 0; ++ priv->curr_duration = 0; + } + return bitrate; + } +@@ -500,7 +517,7 @@ static char *ffmpeg_codec_profile(struct input_plugin_data *ip_data) + { + struct ffmpeg_private *priv = ip_data->private; + const char *profile; +- profile = av_get_profile_name(priv->codec, priv->codec_context->profile); ++ profile = av_get_profile_name(priv->codec, priv->codec_ctx->profile); + return profile ? xstrdup(profile) : NULL; + } + diff --git a/patches/0007-Validate-sample-format-in-ip_open.patch b/patches/0007-Validate-sample-format-in-ip_open.patch new file mode 100644 index 0000000..aad27c8 --- /dev/null +++ b/patches/0007-Validate-sample-format-in-ip_open.patch @@ -0,0 +1,31 @@ +From: ihy123 +Date: Sun, 17 Aug 2025 14:28:46 +0300 +Subject: Validate sample format in ip_open() + +To prevent segfault in ip_setup() because channels=0, validate ip_data->sf +after opening ip. +--- + input.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/input.c b/input.c +index c20cb3f..f5c5b3c 100644 +--- a/input.c ++++ b/input.c +@@ -605,6 +605,16 @@ int ip_open(struct input_plugin *ip) + ip_reset(ip, 1); + return rc; + } ++ ++ unsigned bits = sf_get_bits(ip->data.sf); ++ unsigned channels = sf_get_channels(ip->data.sf); ++ unsigned rate = sf_get_rate(ip->data.sf); ++ if (!bits || !channels || !rate) { ++ d_print("corrupt file: bits = %u, channels = %u, rate = %u\n", ++ bits, channels, rate); ++ return -IP_ERROR_FILE_FORMAT; ++ } ++ + ip->open = 1; + return 0; + } diff --git a/patches/0008-ip-ffmpeg-flush-swresample-buffer-when-seeking.patch b/patches/0008-ip-ffmpeg-flush-swresample-buffer-when-seeking.patch new file mode 100644 index 0000000..89f2cf9 --- /dev/null +++ b/patches/0008-ip-ffmpeg-flush-swresample-buffer-when-seeking.patch @@ -0,0 +1,21 @@ +From: ihy123 +Date: Sun, 17 Aug 2025 14:53:52 +0300 +Subject: ip/ffmpeg: flush swresample buffer when seeking + +--- + ip/ffmpeg.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ip/ffmpeg.c b/ip/ffmpeg.c +index 42f630e..775e7de 100644 +--- a/ip/ffmpeg.c ++++ b/ip/ffmpeg.c +@@ -444,7 +444,7 @@ static int ffmpeg_seek(struct input_plugin_data *ip_data, double offset) + + priv->swr_frame->nb_samples = 0; + avcodec_flush_buffers(priv->codec_ctx); +- /* also flush swresample buffers? */ ++ swr_convert(priv->swr, NULL, 0, NULL, 0); /* flush swr buffer */ + return 0; + } + diff --git a/patches/0009-ip-ffmpeg-remember-swr_frame-s-capacity.patch b/patches/0009-ip-ffmpeg-remember-swr_frame-s-capacity.patch new file mode 100644 index 0000000..e709153 --- /dev/null +++ b/patches/0009-ip-ffmpeg-remember-swr_frame-s-capacity.patch @@ -0,0 +1,38 @@ +From: ihy123 +Date: Sun, 17 Aug 2025 15:02:34 +0300 +Subject: ip/ffmpeg: remember swr_frame's capacity + +--- + ip/ffmpeg.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/ip/ffmpeg.c b/ip/ffmpeg.c +index 775e7de..c659c13 100644 +--- a/ip/ffmpeg.c ++++ b/ip/ffmpeg.c +@@ -49,6 +49,7 @@ struct ffmpeg_private { + + /* A buffer to hold swr_convert()-ed samples */ + AVFrame *swr_frame; ++ int swr_frame_samples_cap; + int swr_frame_start; + + /* Bitrate estimation */ +@@ -213,6 +214,7 @@ static int ffmpeg_init_swr_frame(struct ffmpeg_private *priv, + d_print("av_frame_get_buffer(): %s\n", ffmpeg_errmsg(res)); + return -IP_ERROR_INTERNAL; + } ++ priv->swr_frame_samples_cap = frame->nb_samples; + frame->nb_samples = 0; + + priv->swr_frame = frame; +@@ -378,8 +380,7 @@ static int ffmpeg_convert_frame(struct ffmpeg_private *priv) + { + int res = swr_convert(priv->swr, + priv->swr_frame->extended_data, +- /* TODO: proper buffer capacity */ +- priv->frame->nb_samples, ++ priv->swr_frame_samples_cap, + (const uint8_t **)priv->frame->extended_data, + priv->frame->nb_samples); + if (res >= 0) { diff --git a/patches/0010-ip-ffmpeg-reset-swr_frame_start-when-seeking.patch b/patches/0010-ip-ffmpeg-reset-swr_frame_start-when-seeking.patch new file mode 100644 index 0000000..fd72b0f --- /dev/null +++ b/patches/0010-ip-ffmpeg-reset-swr_frame_start-when-seeking.patch @@ -0,0 +1,20 @@ +From: ihy123 +Date: Sun, 17 Aug 2025 15:54:19 +0300 +Subject: ip/ffmpeg: reset swr_frame_start when seeking + +--- + ip/ffmpeg.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/ip/ffmpeg.c b/ip/ffmpeg.c +index c659c13..71cc511 100644 +--- a/ip/ffmpeg.c ++++ b/ip/ffmpeg.c +@@ -444,6 +444,7 @@ static int ffmpeg_seek(struct input_plugin_data *ip_data, double offset) + return -IP_ERROR_FUNCTION_NOT_SUPPORTED; + + priv->swr_frame->nb_samples = 0; ++ priv->swr_frame_start = 0; + avcodec_flush_buffers(priv->codec_ctx); + swr_convert(priv->swr, NULL, 0, NULL, 0); /* flush swr buffer */ + return 0; diff --git a/patches/0011-ip-ffmpeg-better-frame-skipping-logic.patch b/patches/0011-ip-ffmpeg-better-frame-skipping-logic.patch new file mode 100644 index 0000000..cf01f63 --- /dev/null +++ b/patches/0011-ip-ffmpeg-better-frame-skipping-logic.patch @@ -0,0 +1,151 @@ +From: ihy123 +Date: Sun, 17 Aug 2025 17:27:20 +0300 +Subject: ip/ffmpeg: better frame skipping logic + +--- + ip/ffmpeg.c | 82 ++++++++++++++++++++++++++++++------------------------------- + 1 file changed, 41 insertions(+), 41 deletions(-) + +diff --git a/ip/ffmpeg.c b/ip/ffmpeg.c +index 71cc511..af6ecfb 100644 +--- a/ip/ffmpeg.c ++++ b/ip/ffmpeg.c +@@ -44,8 +44,8 @@ struct ffmpeg_private { + + AVPacket *pkt; + AVFrame *frame; +- int64_t seek_ts; +- int64_t prev_frame_end; ++ double seek_ts; ++ int64_t skip_samples; + + /* A buffer to hold swr_convert()-ed samples */ + AVFrame *swr_frame; +@@ -249,7 +249,6 @@ static int ffmpeg_open(struct input_plugin_data *ip_data) + priv.pkt = av_packet_alloc(); + priv.frame = av_frame_alloc(); + priv.seek_ts = -1; +- priv.prev_frame_end = -1; + + priv.swr = swr_alloc(); + ffmpeg_set_sf_and_swr_opts(priv.swr, priv.codec_ctx, +@@ -283,37 +282,37 @@ static int ffmpeg_close(struct input_plugin_data *ip_data) + return 0; + } + +-/* +- * return: +- * 0 - retry +- * >0 - ok +- */ +-static int ffmpeg_seek_into_frame(struct ffmpeg_private *priv, int64_t frame_ts) ++static int64_t ffmpeg_calc_skip_samples(struct ffmpeg_private *priv) + { +- if (frame_ts >= 0) { +- AVStream *s = priv->format_ctx->streams[priv->stream_index]; +- frame_ts = av_rescale_q(frame_ts, s->time_base, AV_TIME_BASE_Q); ++ int64_t ts; ++ if (priv->frame->pts >= 0) { ++ ts = priv->frame->pts; ++ } else if (priv->frame->pkt_dts >= 0) { ++ ts = priv->frame->pkt_dts; + } else { +- frame_ts = priv->prev_frame_end; ++ d_print("AVFrame.pts and AVFrame.pkt_dts are unset\n"); ++ return -1; + } + +- if (frame_ts >= priv->seek_ts) +- return 1; +- +- int64_t frame_dur = av_rescale(priv->frame->nb_samples, +- AV_TIME_BASE, priv->frame->sample_rate); +- int64_t frame_end = frame_ts + frame_dur; +- priv->prev_frame_end = frame_end; ++ AVStream *s = priv->format_ctx->streams[priv->stream_index]; ++ double frame_ts = ts * av_q2d(s->time_base); + +- d_print("seek_ts: %ld, frame_ts: %ld, frame_end: %ld\n", +- priv->seek_ts, frame_ts, frame_end); ++ d_print("seek_ts: %.6fs, frame_ts: %.6fs\n", priv->seek_ts, frame_ts); + +- if (frame_end <= priv->seek_ts) ++ if (frame_ts >= priv->seek_ts) + return 0; ++ return (priv->seek_ts - frame_ts) * priv->frame->sample_rate; ++} + +- int64_t skip_samples = av_rescale(priv->seek_ts - frame_ts, +- priv->frame->sample_rate, AV_TIME_BASE); +- priv->frame->nb_samples -= skip_samples; ++static void ffmpeg_skip_frame_part(struct ffmpeg_private *priv) ++{ ++ if (priv->skip_samples >= priv->frame->nb_samples) { ++ d_print("skipping frame: %d samples\n", ++ priv->frame->nb_samples); ++ priv->skip_samples -= priv->frame->nb_samples; ++ priv->frame->nb_samples = 0; ++ return; ++ } + + int bps = av_get_bytes_per_sample(priv->frame->format); + #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(59, 24, 100) +@@ -322,15 +321,17 @@ static int ffmpeg_seek_into_frame(struct ffmpeg_private *priv, int64_t frame_ts) + int channels = priv->codec_ctx->channels; + #endif + ++ priv->frame->nb_samples -= priv->skip_samples; ++ + /* Just modify frame's data pointer because it's throw-away */ + if (av_sample_fmt_is_planar(priv->frame->format)) { + for (int i = 0; i < channels; i++) +- priv->frame->extended_data[i] += skip_samples * bps; ++ priv->frame->extended_data[i] += priv->skip_samples * bps; + } else { +- priv->frame->extended_data[0] += skip_samples * channels * bps; ++ priv->frame->extended_data[0] += priv->skip_samples * channels * bps; + } +- d_print("skipping %ld samples\n", skip_samples); +- return 1; ++ d_print("skipping %ld samples\n", priv->skip_samples); ++ priv->skip_samples = 0; + } + + /* +@@ -361,17 +362,16 @@ static int ffmpeg_get_frame(struct ffmpeg_private *priv) + if (res < 0) + return res; + +- int64_t frame_ts = -1; +- if (priv->frame->pts >= 0) +- frame_ts = priv->frame->pts; +- else if (priv->frame->pkt_dts >= 0) +- frame_ts = priv->frame->pkt_dts; ++ if (priv->seek_ts > 0) { ++ priv->skip_samples = ffmpeg_calc_skip_samples(priv); ++ if (priv->skip_samples >= 0) ++ priv->seek_ts = -1; ++ } + +- if (priv->seek_ts > 0 && (frame_ts >= 0 || priv->prev_frame_end >= 0)) { +- if (ffmpeg_seek_into_frame(priv, frame_ts) == 0) ++ if (priv->skip_samples > 0) { ++ ffmpeg_skip_frame_part(priv); ++ if (priv->frame->nb_samples == 0) + return 0; +- priv->seek_ts = -1; +- priv->prev_frame_end = -1; + } + return 1; + } +@@ -434,8 +434,8 @@ static int ffmpeg_seek(struct input_plugin_data *ip_data, double offset) + struct ffmpeg_private *priv = ip_data->private; + AVStream *st = priv->format_ctx->streams[priv->stream_index]; + +- priv->seek_ts = offset * AV_TIME_BASE; +- priv->prev_frame_end = -1; ++ priv->seek_ts = offset; ++ priv->skip_samples = 0; + int64_t ts = av_rescale(offset, st->time_base.den, st->time_base.num); + + int ret = avformat_seek_file(priv->format_ctx, diff --git a/patches/0012-ip-ffmpeg-don-t-process-empty-frames.patch b/patches/0012-ip-ffmpeg-don-t-process-empty-frames.patch new file mode 100644 index 0000000..a52c31c --- /dev/null +++ b/patches/0012-ip-ffmpeg-don-t-process-empty-frames.patch @@ -0,0 +1,21 @@ +From: ihy123 +Date: Sun, 17 Aug 2025 19:22:50 +0300 +Subject: ip/ffmpeg: don't process empty frames + +--- + ip/ffmpeg.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ip/ffmpeg.c b/ip/ffmpeg.c +index af6ecfb..dd9061a 100644 +--- a/ip/ffmpeg.c ++++ b/ip/ffmpeg.c +@@ -356,7 +356,7 @@ static int ffmpeg_get_frame(struct ffmpeg_private *priv) + priv->curr_duration += priv->pkt->duration; + + res = avcodec_send_packet(priv->codec_ctx, priv->pkt); +- if (res == AVERROR(EAGAIN)) ++ if (res == 0 || res == AVERROR(EAGAIN)) + return 0; + } + if (res < 0) diff --git a/patches/0013-ip-ffmpeg-improve-readability.patch b/patches/0013-ip-ffmpeg-improve-readability.patch new file mode 100644 index 0000000..cdad99c --- /dev/null +++ b/patches/0013-ip-ffmpeg-improve-readability.patch @@ -0,0 +1,111 @@ +From: ihy123 +Date: Mon, 18 Aug 2025 03:32:22 +0300 +Subject: ip/ffmpeg: improve readability + +Previously ffmpeg_read()'s while loop was kinda leaking into +ffmpeg_get_frame(), now it doesn't. +--- + ip/ffmpeg.c | 36 ++++++++++++++++++++---------------- + 1 file changed, 20 insertions(+), 16 deletions(-) + +diff --git a/ip/ffmpeg.c b/ip/ffmpeg.c +index dd9061a..fc74895 100644 +--- a/ip/ffmpeg.c ++++ b/ip/ffmpeg.c +@@ -337,30 +337,32 @@ static void ffmpeg_skip_frame_part(struct ffmpeg_private *priv) + /* + * return: + * <0 - error +- * 0 - retry ++ * 0 - eof + * >0 - ok + */ + static int ffmpeg_get_frame(struct ffmpeg_private *priv) + { +- int res = avcodec_receive_frame(priv->codec_ctx, priv->frame); ++ int res; ++retry: ++ res = avcodec_receive_frame(priv->codec_ctx, priv->frame); + if (res == AVERROR(EAGAIN)) { + av_packet_unref(priv->pkt); + res = av_read_frame(priv->format_ctx, priv->pkt); + if (res < 0) +- return res; ++ goto err; + + if (priv->pkt->stream_index != priv->stream_index) +- return 0; ++ goto retry; + + priv->curr_size += priv->pkt->size; + priv->curr_duration += priv->pkt->duration; + + res = avcodec_send_packet(priv->codec_ctx, priv->pkt); + if (res == 0 || res == AVERROR(EAGAIN)) +- return 0; ++ goto retry; + } + if (res < 0) +- return res; ++ goto err; + + if (priv->seek_ts > 0) { + priv->skip_samples = ffmpeg_calc_skip_samples(priv); +@@ -371,9 +373,14 @@ static int ffmpeg_get_frame(struct ffmpeg_private *priv) + if (priv->skip_samples > 0) { + ffmpeg_skip_frame_part(priv); + if (priv->frame->nb_samples == 0) +- return 0; ++ goto retry; + } + return 1; ++err: ++ if (res == AVERROR_EOF) ++ return 0; ++ d_print("%s\n", ffmpeg_errmsg(res)); ++ return -IP_ERROR_INTERNAL; + } + + static int ffmpeg_convert_frame(struct ffmpeg_private *priv) +@@ -386,8 +393,10 @@ static int ffmpeg_convert_frame(struct ffmpeg_private *priv) + if (res >= 0) { + priv->swr_frame->nb_samples = res; + priv->swr_frame_start = 0; ++ return res; + } +- return res; ++ d_print("%s\n", ffmpeg_errmsg(res)); ++ return -IP_ERROR_INTERNAL; + } + + static int ffmpeg_read(struct input_plugin_data *ip_data, char *buffer, int count) +@@ -401,16 +410,14 @@ static int ffmpeg_read(struct input_plugin_data *ip_data, char *buffer, int coun + while (count) { + if (priv->swr_frame->nb_samples == 0) { + res = ffmpeg_get_frame(priv); +- if (res == AVERROR_EOF) ++ if (res == 0) + break; +- else if (res == 0) +- continue; + else if (res < 0) +- goto err; ++ return res; + + res = ffmpeg_convert_frame(priv); + if (res < 0) +- goto err; ++ return res; + } + + int copy_frames = min_i(count, priv->swr_frame->nb_samples); +@@ -424,9 +431,6 @@ static int ffmpeg_read(struct input_plugin_data *ip_data, char *buffer, int coun + written += copy_bytes; + } + return written; +-err: +- d_print("%s\n", ffmpeg_errmsg(res)); +- return -IP_ERROR_INTERNAL; + } + + static int ffmpeg_seek(struct input_plugin_data *ip_data, double offset) diff --git a/patches/0014-ip-ffmpeg-fix-building-for-ffmpeg-8.0.patch b/patches/0014-ip-ffmpeg-fix-building-for-ffmpeg-8.0.patch new file mode 100644 index 0000000..99bd845 --- /dev/null +++ b/patches/0014-ip-ffmpeg-fix-building-for-ffmpeg-8.0.patch @@ -0,0 +1,24 @@ +From: ihy123 +Date: Sun, 24 Aug 2025 19:16:57 +0300 +Subject: ip/ffmpeg: fix building for ffmpeg 8.0 + +avcodec_close() can be safely removed because avcodec_free_context() +is its replacement since 2016. See ffmpeg commit 2ef6dab0a79 + +Builds with v3.3.9 v4.0.6 v6.1.3 v7.1.1 v8.0 +--- + ip/ffmpeg.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/ip/ffmpeg.c b/ip/ffmpeg.c +index fc74895..2cb0767 100644 +--- a/ip/ffmpeg.c ++++ b/ip/ffmpeg.c +@@ -223,7 +223,6 @@ static int ffmpeg_init_swr_frame(struct ffmpeg_private *priv, + + static void ffmpeg_free(struct ffmpeg_private *priv) + { +- avcodec_close(priv->codec_ctx); + avcodec_free_context(&priv->codec_ctx); + avformat_close_input(&priv->format_ctx); + diff --git a/patches/0015-ip-ffmpeg-change-sample-format-conversions.patch b/patches/0015-ip-ffmpeg-change-sample-format-conversions.patch new file mode 100644 index 0000000..5235212 --- /dev/null +++ b/patches/0015-ip-ffmpeg-change-sample-format-conversions.patch @@ -0,0 +1,30 @@ +From: ihy123 +Date: Mon, 25 Aug 2025 11:17:06 +0300 +Subject: ip/ffmpeg: change sample format conversions + +--- + ip/ffmpeg.c | 10 ++++------ + 1 file changed, 4 insertions(+), 6 deletions(-) + +diff --git a/ip/ffmpeg.c b/ip/ffmpeg.c +index 2cb0767..2d3c610 100644 +--- a/ip/ffmpeg.c ++++ b/ip/ffmpeg.c +@@ -157,13 +157,11 @@ static void ffmpeg_set_sf_and_swr_opts(SwrContext *swr, AVCodecContext *cc, + av_opt_set_int(swr, "in_sample_rate", cc->sample_rate, 0); + av_opt_set_int(swr, "out_sample_rate", out_sample_rate, 0); + +- *out_sample_fmt = cc->sample_fmt; +- switch (*out_sample_fmt) { +- case AV_SAMPLE_FMT_U8: +- sf |= sf_bits(8) | sf_signed(0); +- break; +- case AV_SAMPLE_FMT_S32: ++ switch (cc->sample_fmt) { ++ case AV_SAMPLE_FMT_FLT: case AV_SAMPLE_FMT_FLTP: ++ case AV_SAMPLE_FMT_S32: case AV_SAMPLE_FMT_S32P: + sf |= sf_bits(32) | sf_signed(1); ++ *out_sample_fmt = AV_SAMPLE_FMT_S32; + break; + default: + sf |= sf_bits(16) | sf_signed(1); diff --git a/patches/series b/patches/series new file mode 100644 index 0000000..c78695c --- /dev/null +++ b/patches/series @@ -0,0 +1,15 @@ +0001-atomic_ld.patch +0002-fix-blhc.patch +0003-ip-ffmpeg-more-precise-seeking.patch +0004-ip-ffmpeg-skip-samples-only-when-needed.patch +0005-ip-ffmpeg-remove-excessive-version-checks.patch +0006-ip-ffmpeg-major-refactor.patch +0007-Validate-sample-format-in-ip_open.patch +0008-ip-ffmpeg-flush-swresample-buffer-when-seeking.patch +0009-ip-ffmpeg-remember-swr_frame-s-capacity.patch +0010-ip-ffmpeg-reset-swr_frame_start-when-seeking.patch +0011-ip-ffmpeg-better-frame-skipping-logic.patch +0012-ip-ffmpeg-don-t-process-empty-frames.patch +0013-ip-ffmpeg-improve-readability.patch +0014-ip-ffmpeg-fix-building-for-ffmpeg-8.0.patch +0015-ip-ffmpeg-change-sample-format-conversions.patch diff --git a/rules b/rules new file mode 100755 index 0000000..765979d --- /dev/null +++ b/rules @@ -0,0 +1,59 @@ +#!/usr/bin/make -f +# -*- makefile -*- + +include /usr/share/dpkg/architecture.mk +ifneq ($(DEB_BUILD_ARCH),$(DEB_HOST_ARCH)) +CROSS = CROSS=$(DEB_HOST_GNU_TYPE)- PKG_CONFIG=$(DEB_HOST_GNU_TYPE)-pkg-config +endif + +# The following architectures need the -latomic flag +# to build, otherwise we FTBFS with +# ./track_info.c:47: undefined reference to `__atomic_fetch_add_8' +# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=935678 +# Depends on 13-atomic_ld.patch +ifneq (,$(findstring $(DEB_HOST_ARCH), armel m68k mipsel powerpc sh4)) +export LDLIBS += -latomic +endif + +DEB_CFLAGS_MAINT_APPEND += -I/usr/include/ncursesw +DEB_BUILD_MAINT_OPTIONS = hardening=+all +DPKG_EXPORT_BUILDFLAGS = 1 +include /usr/share/dpkg/buildflags.mk + +suggested_deps = pulse jack + +EXTRA_CMUS_DIR_OP_PLUGINS = debian/cmus/usr/lib/cmus/op/ +EXTRA_CMUS_PLUGINS := $(foreach plugin,$(suggested_deps),$(plugin).so) + +%: + dh $@ --with bash-completion + +override_dh_auto_configure: + $(CROSS) ./configure \ + prefix=/usr \ + CONFIG_ARTS=n \ + CONFIG_ROAR=n \ + DEBUG=0 + +override_dh_auto_build: + # Pass V=2 to make to enable verbose build logs, which is useful for + # porters, sorting out build hardening issues, etc. + dh_auto_build -- V=2 + +override_dh_install: + dh_install -pcmus + dh_movefiles -pcmus-plugin-ffmpeg --sourcedir=debian/cmus/ \ + /usr/lib/cmus/ip/ffmpeg.so + +override_dh_installdocs: + dh_installdocs + # do not install zsh and bash completion twice + rm debian/cmus/usr/share/doc/cmus/contrib/_cmus \ + debian/cmus/usr/share/doc/cmus/contrib/cmus.bash-completion + +override_dh_shlibdeps: + dh_shlibdeps -pcmus $(foreach plugin,$(EXTRA_CMUS_PLUGINS),-X$(plugin)) + dpkg-shlibdeps -O -dSuggests \ + $(foreach plugin,$(EXTRA_CMUS_PLUGINS),$(EXTRA_CMUS_DIR_OP_PLUGINS)$(plugin)) \ + >> debian/cmus.substvars + dh_shlibdeps --remaining-packages diff --git a/salsa-ci.yml b/salsa-ci.yml new file mode 100644 index 0000000..8424db4 --- /dev/null +++ b/salsa-ci.yml @@ -0,0 +1,3 @@ +--- +include: + - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/recipes/debian.yml diff --git a/source/format b/source/format new file mode 100644 index 0000000..163aaf8 --- /dev/null +++ b/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/upstream/metadata b/upstream/metadata new file mode 100644 index 0000000..7d5f13d --- /dev/null +++ b/upstream/metadata @@ -0,0 +1,4 @@ +Bug-Database: https://github.com/cmus/cmus/issues +Bug-Submit: https://github.com/cmus/cmus/issues/new +Repository: https://github.com/cmus/cmus.git +Repository-Browse: https://github.com/cmus/cmus diff --git a/watch b/watch new file mode 100644 index 0000000..dca9c32 --- /dev/null +++ b/watch @@ -0,0 +1,3 @@ +version=4 +opts="dversionmangle=s/\+git\d+$//,uversionmangle=s/([\d\.]+)-(rc|beta)(.*)/\1~\2\3/,filenamemangle=s/.+\/v?(\d\S*)\.tar\.gz/cmus-$1\.tar\.gz/" \ +https://github.com/cmus/cmus/tags .*/v?(\d\S*)\.tar\.gz